Hogyan cseréljünk le egy szövegben adott karaktereket másra?

A feladatot több módon is megoldhatjuk. Az első, ami eszünkbe juthat, hogy vesszük sorban egymás után a szöveg karaktereit, és ellenőrizzük, hogy az aktuális karakter megtalálható-e a lecserélendő karakterek között. Ha igen, akkor a karakterláncokra értelmezett replace() metódussal kicseréljük ezt, és az így módosított karakterlánccal folytatjuk a vizsgálatot és szükség esetén a cserét. Ezt mutatja az alábbi modul első függvénydefiníciója.

Az első megoldás abból a szempontból nem előnyös, hogy többször végigjárja ugyanazt a szöveget. Amikor a replace() ténylegesen cserét hajt végre, új karakterláncot hoz létre, ami teljes másolással jár. Emiatt több memóriafoglalás és másolás történik, mint a többi megoldásnál.

A második és harmadik függvényben ezt úgy küszöböljük ki, hogy egy generátorkifejezéssel előállítjuk a cserélt karakterek sorozatát, amit a join() metódussal egyesítünk karakterlánccá.

A karaktercserét a második függvénynél egy feltételes kifejezéssel valósítjuk meg, melyben ellenőrizzük, hogy a szöveg soron következő karakterét le kell-e cserélni. Ha nem, akkor a generátor aktuális eleme e karakter lesz. Ha pedig igen, akkor a cserélendő karakterek sorozatában kikeressük a find() metódussal az adott karakter indexét, amivel kikérjük a helyettesítő karakterláncban a megfelelő karaktert.

A harmadik függvénynél a karaktercserét úgy végezzük el, hogy először is létrehozzuk a lecserélendő és helyettesítő karaktereket összerendelő szótárt. A generátor aktuális elemét e szótár get() metódussal kiolvasott értéke adja. Itt kihasználjuk azt, hogy a get() metódusnak lehet alapértelmezett értéket adni, ami jelen esetben a szöveg éppen soron következő karaktere. Ezzel azt érjük el, hogy ha a szöveg soron következő karakterével mint kulccsal hívjuk meg a get() metódust, akkor ha az benne van a szótárban, vagyis lecserélendő, akkor a helyettesítő karakter lesz a visszatérési érték. Ha pedig nincs a szótárban, vagyis nem cserélendő karakterről van szó, akkor az alapértelmezett értékkel tér vissza, ami önmaga.

A fenti megoldásoknak az a hátránya, hogy a szöveg karaktereinek feldolgozása Python-szinten történik, ezért hosszú szövegeknél a sok iteráció jelentős többletidőt okozhat. Ezen segítenek a negyedik, ötödik és hatodik függvényben látható megoldások.

A negyedik függvénynél a cserélendő karaktereket vesszük egymás után, amelyek száma viszonylag kevés és független a vizsgált szöveg hosszától. A szöveg karaktereit pedig nem Python-kódban járjuk végig, hanem a cserélendő karakterekhez tartozó replace() hívásokra bízzuk a teljes bejárást. De a replace() hívást is csak akkor hajtjuk végre, ha az éppen sorra vett cserélendő karakter előfordul a szövegben. Továbbá kihasználjuk a replace() azon jellemzőjét, hogy a helyettesítést a lecserélendő karakter minden előfordulására elvégzi.

Az ötödik függvényben még egyszerűbben járunk el. Vesszük a cserélendő és helyettesítő karakterpárokat, és minden ilyen párra meghívjuk a replace() metódust.

Az utolsó, hatodik megoldás a legtömörebb, ahol a karakterlánc translate(), valamint a str típus maketrans() metódusait használjuk.

Az alábiakban a funkcionális teszt mellett a futási idő mérés kódsorait és eredményét láthatjuk.

A végrehajtási időkre kapott eredmények visszaigazolják, hogy az első három, azaz a szöveg karaktereit egyenként vizsgáló megoldás lassabb futású, mint a másik három változat. Ez utóbbiak közül az ismételt replace() hívásokra épülő függvények a mérések szerint gyorsabbak, mint a translate() metódust használó.

Mivel mindhárom megoldás a CPython C-ben implementált beépített karakterlánc-műveleteire épül, a különbség valószínűleg a belső implementációk eltérő optimalizációiból adódik.

A beépített függvények és metódusok végrehajtási ideje Python-verziónként is eltérhet. Ennek oka, hogy az egyes verziókban a belső implementációk változhatnak, ezért ugyanaz a kód eltérő teljesítményt adhat. Jó példa erre a translate() metódus. Ha az ezt használó replace_chars6() függvény futási idejét megmérjük Python 3.10, 3.11, 3.12, 3.13 és 3.14 verziókkal, akkor a 3.14-hez képest a korábbi verziók nagyobb futási időket mutatnak. Python 3.12 és 3.13 esetén a translate() futási ideje már megközelíti az első három függvényét.

Ezzel szemben a replace() metódust használó függvények a vizsgált Python verziókban hasonló végrehajtási idővel futnak. Ezért a bemutatott környezetben érdemes ezeket a megoldásokat választani, és közülük is a leggyorsabbnak bizonyuló replace_chars4() és replace_chars5() függvényeket.

A replace(), translate() és maketrans() használatának és lehetőségeinek példákkal illusztrált bővebb magyarázata – a karakterláncok többi metódusainak kifejtésével együtt – megtalálható a Python tudásépítés lépésről lépésre az alapoktól az első asztali alkalmazásig e-könyv „Beépített típusok nyilvános metódusai” fejezetében a karakterláncok metódusait tárgyaló alcím alatt.

Érdekel a Python tudásépítés lépésről lépésre az alapoktól az első asztali alkalmazásig című e-könyv.