Kivételkezelő szerkezetet vagy előzetes feltételvizsgálatot használjunk?

Minden programnak van egy tervezett ideális normál lefolyása, ami akkor valósul meg, ha minden az elvárások szerint történik. Ez utóbbi azt jelenti például, hogy egy függvény mindig a megfelelő típusú és értékű argumentumot kap, a felhasználó nem hibázik az adatbevitelnél, a fájl mindig létezik az adott néven, vagy egy hálózati kapcsolat soha nem szakad meg.

A valóságban ezek az ideális feltételek nem mindig valósulnak meg. Ilyekor az ideális esethez képest kivételes események történnek. Ahhoz, hogy a program ilyenkor se omoljon össze, a kivételes eseményekre fel kell készíteni. Erre alapvetően két mód van:

1) előzetesen ellenőrizzük a helyes működéshez szükséges feltételek fennállását egy feltételvizsgálattal, amit általában az if…else… szerkezettel teszünk.

2) kivételkezelő szerkezetet alkalmazunk, amelyet Pythonban a try…except…finally… valósít meg.

Ez a két módszer elvi szinten a problémamegoldás megközelítési módjában tér el. Az 1) módszer elébe megy a problémának, nem is engedi, hogy bekövetkezzen. A 2) módszer ezzel szemben inkább reagáló, mert engedi a kivételes eseményt egy adott ponton hatni, de azt annak bekövetkezésekor azonnal lekezeli egy megfelelő programág lefuttatásával.

E két megközelítési módot szemléletesen el is nevezték a Pythonban. Az 1) módszerre az angol LBYL (look before you leap) rövidítéssel szoktak hivatkozni, a 2) módszerre pedig az EAFP (easier to ask for forgiveness than permission) betűszóval.

Tehát, az LBYL az „Először gondolkodj, azután cselekedj” bölcsességet követő megelőző stratégia. Az EAFP viszont inkább úgy áll hozzá, hogy „Ne parázz előre, ha esetleg tényleg gond lenne, majd akkor foglalkozunk vele és megoldjuk”.

A címben feltett kérdésre, azaz, hogy a kettő közül melyiket válasszuk, mint sok más esetben is a korrekt válasz az, hogy attól függ. Már csak azt kell tudni, hogy mitől.

Az egyik lehetséges szempont, hogy a kivételes eseményt mi idézi elő, honnan származik.

Azokban az esetekben, ahol a kivételes esemény hatását csak összetett feltételvizsgálattal lehetne megelőzni, ott célszerűbb a kivételkezelő szerkezet alkalmazása. Ilyen például egy fájl használatra való megnyitásakor fellépő probléma. Ennek számos oka lehet, pl.: nem létezik a fájl, módosult a fájlnév, megváltozott a jogosultság. Ahelyett, hogy ezeket mind előre ellenőriznénk (létezik-e a megnyitni kívánt fájl, létezik-e az adott néven, és a szükséges jogosultsággal) egyszerűbb egy kivételkezelő szerkezetben detektálni a hibát, és ha bekövetkezik megfelelő intézkedéseket tenni.

Akkor is érdemes az EAFP stratégiát követni, ha fennállhat a veszélye annak, hogy amíg az if…else… ellenőrző kódok lefutnak és nem jeleznek gondot, a körülmények megváltoznak, és így mégis probléma léphet fel. Ez általában olyan kivételes eseményeknél fordulhat elő, amelyek külső forrásból származnak, és amelyekre nincs ráhatásunk. Ilyen a már említett fájlmegnyitás, vagy például hálózati kapcsolat használata. Ugyanis megtörténhet, hogy az ellenőrző kódok futása alatti rövid időben törlődik a fájl, vagy szakad meg a hálózati kapcsolat.

Egy további választási szempont, hogy a kivételes esemény bekövetkezésének valószínűsége mekkora. Ha viszonylag gyakori előfordulásra számítunk, akkor célszerűbb inkább az LBYL alkalmazása. Fordított esetben, vagyis, ha viszonylag ritkán lehet a kivételes eseményre számítani, akkor az EAFP módszer ajánlott. Már csak azért is, mert

a) az LBYL-t megvalósító feltételes elágazások csökkentik a kód áttekinthetőségét és olvashatóságát, különösen, ha több a védőkód mint a védendő. Továbbá, ha ritka a kivételes esemény, akkor is mindig le kell futni az ellenőrző kódoknak, ami a futási időt növeli.

b) A try…except… szerkezet használata csak akkor költségesebb futási időben, mint az if…else…, ha a kivételt „el kell kapni”, azaz a kivételkezelő ágra kell a vezérlést irányítani. Ehhez láthatunk alább egy összehasonlító tesztet.

Az LBYL és EAFP stratégiák alkalmazásával a hétköznapokban is találkozhatunk. Az LBYL megközelítést láthatjuk működni például egy repülőgépre való felszállás előtt a biztonsági ellenőrzés során. Itt nem megengedhető, hogy akárcsak egyszer is előforduljon a kivételes esemény (robbanószer vagy fegyver felvitele). Itt ez a szükséges módszer, de saját bőrünkön tapasztalhatjuk a hátrányát is (sorban állás, várakozás), ami abból adódik, hogy bár a kivételes esemény gyakorisága szerencsére viszonylag ritka, ennek ellenére mindenkit át kell vizsgálni. Ezzel szemben egy áruházban, bár van biztonsági őr, nem ellenőriznek minden be- vagy kilépőt. Csak akkor történik a megszokottól eltérő cselekménysor, intézkedés, ha kivételes eseményt (pl. lopás, rongálás) észlelnek.

E bejegyzés témájához a Python tudásépítés lépésről lépésre című e-könyv következő fejezetei kapcsolódnak: „Válaszutak – elágazások a programban” és „Kivételes bánásmód – kivételek és kezelésük” fejezetek, valamint a „Készétel fogyasztás – a szabványos könyvtár moduljainak használata” fejezeten belül a „A programvégrehajtás felfüggesztése és a futási idő mérése” alfejezet.

Érdemes még a Python hivatalos dokumentációjában a Fogalomtár (Glossary) EAFP és LBYL címeit, valamint a FAQ részben a „How fast are exceptions?” részt elolvasni.

É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.