2016. április 11., hétfő

08 - Hibakezelés

Hibák kezelése

Eddig tökéletes, hibátlan szoftvereket fejlesztettünk, mert ezek egyszerű kis kódok voltak, amik általában nem is függtek olyan adattól amit a felhasználótól kaphattunk. Ezért nem is nagyon volt minek elromlania.

A valódi életben viszont a valami mindig el fog baszódni. Jobb esetben a program futtatása előtt, rosszabb esetben (és általában) futás közben.

Háromféle hiba keletkezhet a programban: szintaktikai, futásidejű és szemantikai (logikai).

Szintaktikai hiba az, amikor a forráskódunk hibás: nem megfelelő a behúzás, elmarad a zárójel, vessző, és ehhez hasonló dolgok, amik megakadályozzák a Pythont hogy elolvassa a forráskódot. Az ilyen hibák nem engedik hogy elinduljon a program, viszont elég jól behatárolhatóak, mert a Python meg tudja mondani, hogy melyik volt az a sor és karakter, ahol valami szokatlan/nem várt parancs állt.

A futásidejű hiba az, amikor a programunk helyesen fut, de aztán egyszer csak valami nem várt esettel találkozik a program. Rengeteg ilyen hiba keletkezhet akkor, ha a felhasználótól várunk adatra. Például felszólítjuk az usert, hogy gépeljen be egy dátumot, de ő nem olyan formátumban teszi ezt meg amire az algoritmust írtunk. Ekkor rövid időn belül hibát jelez a Python, mert mondjuk a begépelt adatban nem annyi számjegy van mint amire számítottunk; kötőjel helyett szóköz van és ezért nem tudjuk a dátumot komponensekre szétbontani. Remélem érezhető: igen nehéz minden esetre felkészülni, de a lényeg, hogy a nem várt adat előbb-utóbb mindenképp megakasztja az algoritmust, amit a Python jó vastag hibaüzenettel ad majd a tudtunkra.

A szemantikai hiba a legtrükkösebb. Ilyenkor a forráskód helyes, viszont a program “nem azt csinálja amit akarunk” (természetesen a program mindig azt és csak is azt csinálja amit a forráskódban leírtunk). Ezeket a hibákat csak akkor tudjuk kijavítani, ha átolvassuk és újra értelmezzük az algoritmusunkat, esetleg teljesen újat írunk ugyan annak a problémának a megoldására. A lényeg, hogy szemantikai hiba miatt nem kapunk majd hibaüzenetet, ezért nagyon nehezen detektálhatóak az ilyen hibák, e miatt igen kártékonyak tudnak lenni (mert csak akkor vesszük észre hogy valami rosszul működik, mire az látható kárt okozott).

A hibákat a hétköznapokban bug-nak szokták hívni.

Szintaktikai hibák

A szintaktikai hibák száma annál kevesebb minél többet programoz az ember, ezért ezekkel csak egy példa erejéig foglalkozunk. Vegyük az alábbi hibás kódot:

print(Hello world)

A probléma hogy a Hello World nem string, mivel nincs idézőjelek közé téve. Ha futtatjuk a programot, akkor valami ilyesmit kapunk a konzolba:

  File "/home/slapec/.PyCharm2016.1/config/scratches/blog.py", line 1
    print(Hello world)
                    ^
SyntaxError: invalid syntax

Pythonban ezt a szöveget traceback-nek nevezik. A következő fejezetben részletesen át is nézzük a részeit. Egyelőre viszont elég az utolsó sort olvasni, mert többnyire itt van a lényeg. Ez az esetünkben a SyntaxError: invalid syntax szöveg. Igen csekély angol tudással is látható, hogy a hibát a helytelen szintaxis okozza. Soronként felfelé haladva látszódik az a részlete a forráskódnak, ahol a hiba érzékelhetővé vált, majd legfelül az is, hogy melyik fájl melyik sorában történt a hiba. Így már csak oda kell menni és ki kell javítani.

Szintaktikai hiba keletkezése

A fenti tracebackben a ^ jellel jelölték, hogy a forráskódban hol keletkezett szintaktikai hiba. Ez a jel a world szó végére mutat. Ez nem véletlenül van így: a Hello szónál nem beszélünk még hibáról, hisz a Hello idézőjelek nélkül akár értelmezhető lehetne változóként is. Viszont a függvény argumentumai között vesszőnek kell állnia, ami hiányzik. A forráskód értelmezése eljut függvény hívásának végéig, így biztossá válik, hogy már nem is lesz vessző az argumentumok között, így tehát a world szó az, amire nem számít a Python, és létrejön a SyntaxError.

A mesének az a lényege, hogy a Python mindig azt a pontot mutatja meg, ahol már biztos hogy hibás a szintaxis. Előfordulhat, hogy ez sorokkal később válik csak nyilvánvalóvá:

print(int('10')

Itt lemaradt a print() bezáró zárójele. A következő tracebacket kapjuk:

  File "/home/slapec/.PyCharm2016.1/config/scratches/blog.py", line 2

                   ^
SyntaxError: unexpected EOF while parsing

Bizony, ez a nagy büdös semmire mutat. Az unexpected EOF while parsing üzenet azt jelenti, hogy korábban lett vége a fájlnak mint amire a Python számított (Az EOF az End Of File kifejezés rövidítése).

Ez azért történhetett, mert a Python a print() lezáró zárójelét keresete, de közben véget ért a fájl, így az már sose lesz meg.

Szemantikai hibák

A szemantikai (logikai) hibák úgy kerülhetőek el a legkönnyebben, ha nem írunk rossz kódot. Na persze, ezt aztán baromi könnyű mondani, de megcsinálni azért már nem annyira.

Gondos tervezéssel és megfelelő teszteléssel kellően jól megbizonyosodhatunk róla hogy az algoritmusunk jól működik. A forráskód tesztelésére is kínál eszközöket a Python, de egyelőre nem foglalkozunk ezeknek a használatával, mert arról megint egy jó vastag bejegyzésre való szöveget össze lehetne hordani.

Egyelőre tehát igyekezzünk jó algoritmusokat írni, és keressük a hibákat végig a fejlesztés során. Azaz készüljön el a program egy kisebb része, próbáljuk ki hogy megfelelően működik-e, majd folytassuk az építkezést. Így még a program alapjainál kiderülhet ha valami rosszul működik.

Ha beütött a baj, és látszólag minden megfelelőnek tűnik, akkor jöhetnek az úgynevezett debugger programok. Pythonban ilyenkor a program futása minden soron megáll, és megvizsgálhatjuk a memória tartalmát, láthatjuk hogy a program egy elágazásban milyen irányba fog haladni, és egyéb érdekes dolgok.

Szemantikai hibák keletkezése

Vegyünk egy tipikus logikai hibát, amitől a programunk végtelen ciklusba fog kerülni:

i = 0
while i != 10:
    print(i)

A probléma az, hogy elfelejtettük növeli az i változó értékét, így sosem fog a program kilépni a ciklusból.

A fenti példa még elég triviális, a következő viszont már nem biztos:

i = 0
while i != 10:
    print(i)
    i += 0.1

Ebben a kis kódban 0.1-esével növeljük az i változót, amíg az el nem éri a 10-et. Valamiért azonban mégis átugorja a Python a feltételt és elkezd számolni mint a barom felfelé.

Ha sikerül elkapni az első pár számot ami a konzolba kerül, akkor talán beugrik valami:

0
0.1
0.2
0.30000000000000004
0.4
0.5
0.6
0.7
0.7999999999999999
0.8999999999999999
0.9999999999999999
1.0999999999999999

A float típusnál említettem, hogy a lebegőpontos számok pontossága nem végtelen. Pontosan e miatt történt a baj is: összeadódnak a törtek hibái, és ezért az i változóban levő tört sose lesz majd pontosan 10.

Futásidejű hibák

Igazából az egész bejegyzésnek a fő témája a futásidejű hibák kezelése.

A programozás során kétféleképp győződhetünk meg róla, hogy nem okoz-e hibát az algoritmusunk: először megnézzük hogy szabad-e elvégezni a műveletet és utána végrehajtjuk, vagy először végrehajtjuk és utólag kezeljük a hibát ha keletkezett.

Az előbbi módszert LBYL-nek (Look before you leap (Először gondolkodj, aztán cselekedj)), az utóbbit EAFP-nek (Easier to ask for forgiveness than permission (Könnyebb bocsánatot kérni mint engedélyt)) nevezik.

A Python nyelvben mindkét filozófia megvalósítható, de a nyelv készítői az utóbbi megoldást javasolják.

Személy szerint eléggé keverten használom a két módszert.

Futásidejű hiba keletkezése

Vegyük a korábban felvázolt problémát: kérjük be a felhasználótól a mai dátumot a következő formátumban: ÉÉÉÉ-HH-NN (év, hónap, nap, számokkal, kötőjellel elválasztva), és írjuk vissza a konzolba a magyar helyesírásnak megfelelően.

months = {
    '01': 'január',
    '02': 'február',
    '04': 'március',
    '05': 'április',
    '06': 'június',
    '07': 'július',
    '08': 'augusztus',
    '09': 'szeptember',
    '10': 'október',
    '11': 'november',
    '12': 'december'
}

today = input('A mai dátum: ')

parts = today.split('-')
year, month, day = parts
month_text = months[month]

print('{0}. {1} {2}.'.format(year, month_text, day))

Ahhoz hogy a számjegyekkel felírt hónapot magyar szavakra átalakítsuk felsoroltam az összes hónapot a months dict-ben. Arra számítok hogy ha az aktuális hónap egy számjegyű akkor a bal oldalára nullát írnak majd. A kulcsok azért str-ek és nem int-ek, mert így megspóroltam a begépelt hónap int-é alakítását.

Akkor futtassuk a programot és íruk be pl hogy 2016-04-10:

A mai dátum: 2016-04-10
2016. március 10.

Ez bizony elég szépen ment. Akkor most gépeljük be a dátumot de kötőjelek helyett pontokkal, így: 2016.04.10.

A mai dátum: 2016.04.10.
Traceback (most recent call last):
  File "/home/slapec/.PyCharm2016.1/config/scratches/blog.py", line 18, in <module>
    year, month, day = parts
ValueError: not enough values to unpack (expected 3, got 1)

Meg is érkeztünk, ez bizony elég szépen meghalt. Kezdjük a tűzoltást a hibaüzenet értelmezésével.

A traceback értelmezése

Szerintem a Python nagyon jó és informatív üzenetet ad hiba esetén. Végigkövethető benne a hiba teljes útvonala egész addig amíg az össze nem omlasztotta a programot.

A traceback két részből áll: a legutolsó sora mutatja a hiba típusát, és a hozzá tartozó rövid hibaüzenetet. A fenti példában tehát ez az:

ValueError: not enough values to unpack (expected 3, got 1)

A hiba típusa a kettőspontig tart, ami ebben az esetben a ValueError. Ez nem csak egy egyszerű szó, hanem egy konkrét Python osztály megnevezése, de egyelőre még hívjuk csak típusnak. Több ilyen típus is található beépítve a Pythonban.

A kettőspont után következik maga a hibaüzenet. Ez általában valamilyen angol mondat, nálunk most ez: not enough values to unpack (expected 3, got 1).

Az üzenet azt jelenti, hogy nincs elég elem amit unpack-elni lehetne (3 kéne de csak 1 van). Ezt a problémát mindjárt orvosoljuk, de előbb vegyük a traceback többi részét.

A hibaüzenet feletti sorok ezek voltak:

Traceback (most recent call last):
  File "/home/slapec/.PyCharm2016.1/config/scratches/blog.py", line 18, in <module>
    year, month, day = parts

Itt látható hogy melyik fájlban ("/home/slapec/.PyCharm2016.1/config/scratches/blog.py"), melyik sorban (18), melyik modulban (<module>) történt a hiba, alatta pedig az a konkrét sora a forráskódnak, ahol a hiba létrejött.

A <module> ebben az esetben azt jelenti, hogy közvetlenül a folyó forráskódban volta a hiba.

Ez egy elég rövid kis traceback volt, mivel a hiba nem valamelyik függvény (vagy más egyéb struktúra) mélyén keletkezett. Írjuk át kicsit a kódot, hogy vastagabb hibaüzenetet is lássunk:

def get_month(month):
    months = {
        '01': 'január',
        '02': 'február',
        '04': 'március',
        '05': 'április',
        '06': 'június',
        '07': 'július',
        '08': 'augusztus',
        '09': 'szeptember',
        '10': 'október',
        '11': 'november',
        '12': 'december'
    }

    return months[month]

def pretty_date(today):
    parts = today.split('-')
    year, month, day = parts
    month_text = get_month(month)

    return '{0}. {1} {2}.'.format(year, month_text, day)


today = input('A mai dátum: ')
print(pretty_date(today))

Annyi változtatás történt, hogy a hónap átalakítása, és a magyar formátumra alakítás egy-egy függvénybe került (get_month() és pretty_date()). Vegyük észre, hogy a pretty_date() hívja majd meg a get_month()-ot.

Most írjuk be a dátumot például így: 2014-4-10.

A mai dátum: 2016-4-10
Traceback (most recent call last):
  File "/home/slapec/.PyCharm2016.1/config/scratches/blog.py", line 27, in <module>
    print(pretty_date(today))
  File "/home/slapec/.PyCharm2016.1/config/scratches/blog.py", line 21, in pretty_date
    month_text = get_month(month)
  File "/home/slapec/.PyCharm2016.1/config/scratches/blog.py", line 16, in get_month
    return months[month]
KeyError: '4'

Ez rögtön érdekesebben néz ki. A hiba típusa most a KeyError. Ilyet akkor kapunk ha nem létező kulcsot próbálunk elérni egy dict-ben. A hibaüzenet egyszerűen annyi hogy '4'. Így össze is áll a kép: a '4' kulcs valahonnan hiányzik. Mindjárt az is meglesz hogy honnan.

Láthatóan 3 sor tartalmaz fájlnevet, de ezek mind ugyan arra a fájlra mutatnak. Általában elmondható, hogy ezeket a sorokat lentről-felfelé érdemes olvasni. Ha így haladunk, akkor legelőször azt a sort látjuk, ahol ténylegesen létrejött a hiba. Ahogy haladunk felfelé, úgy követhetjük hogy a hiba hogyan gyűrűdzik végig a programon.

  • A program a 16. sorban létrejött a hiba a return months[month] utasításnál. Ez a get_month modulban (függvényben) van. Így már tiszta is, hogy a months dict-ből hiányzott a kulcs.
  • Ide úgy jutottunk, hogy a 21. sorban a month_text = get_month(mont)-nél meghívtuk a hibázó függvényt. Ez a pretty_date modulban (függvényben) történt.
  • A 21. sorba pedig a 27. sorból ugrottunk. Ez a <module> modulban van, tehát minden függvényen kívül.

Jól látható, hogy maga a hiba igen mélyen történt, az mégis egészen addig halad felfelé a programban, amíg a program ki nem lép.

A hiba kezelése az a művelet lesz, amikor ezt a hibát az útja során valahol elkapjuk, hogy ne omlassza össze a programot.

A hiba keletkezését hétköznapian úgy mondjuk hogy az algoritmus hibát dob. Angolul, a Pythonos elnevezéseknek megfelelően ezt úgy mondják, hogy raise.
Az egyértelmű hogy a raise nem azt jelenti hogy dobni. Más programozási nyelvekben a raise helyett a throw kifejezést használják és ennek a szónak a magyar változata épült be a beszédbe.
Hasonlóan, a hibát elkapni fogjuk. Ennek a Pythonos elnevezése az except lesz, ami szintén köszönő viszonyban sincs az elkapni kifejezéssel. Ugyan úgy, az eltérő programozási nyelvekben használt catch kifejezés vált elfogadottá a beszédben.

A hiba kezelése

Azon a ponton ahol létrejött a hiba véget ér a kód futása, és egy hiba objektum (exception) indul felfelé a programban. Ilyenkor különleges állapotba kerül a Python: ha nem kapjuk el a hibát, akkor a hibakezelő algoritmusok kivételével semmilyen kód sem fut tovább, csak az objektum halad felfelé. Ha sehol se kaptuk el a hibát, akkor végül mindenképp kilép a program (így látszódik majd minden sor a traceback-ben amin végighaladt a hiba).

Az algoritmusunkat (illetve annak tetszőleges részét) úgynevezett try blokkba rakva jelezhetjük a Pythonnak, hogy a blokkban előfordulhat hiba. Ha a blokkban hiba történik, akkor a futás automatikusan a hibakezelő ágakra kerül. Itt típusonként különböző módon foglalkozhatunk a hibával. Ha nem volt olyan ág, ahol a hibát lekezeltük volna, akkor a hiba halad tovább felfelé.

A korábbi traceback-ben három sor is látható volt, így jól megbecsülhető, hogy három helyen kaphattuk volna el a hibát. Az egyszerűség kedvéért most a program végén tesszük ezt meg. Lássuk az új struktúrát!

def get_month(month):
    months = {
        '01': 'január',
        '02': 'február',
        '04': 'március',
        '05': 'április',
        '06': 'június',
        '07': 'július',
        '08': 'augusztus',
        '09': 'szeptember',
        '10': 'október',
        '11': 'november',
        '12': 'december'
    }

    return months[month]

def pretty_date(today):
    parts = today.split('-')
    year, month, day = parts
    month_text = get_month(month)

    return '{0}. {1} {2}.'.format(year, month_text, day)


today = input('A mai dátum: ')

try:
    print(pretty_date(today))
except KeyError:
    print('Hibás hónap!')

Semmi sem változott, az utolsó 4 sor kivételével:

  1. A try-al kezdtük az új blokkot.
  2. Hiba esetén az except sorra ugrik a program. Az utasítás mellé a hiba típusa kerül, aminek bekövetkeztében a program futásának itt kell folytatódnia. Látható, hogy a KeyError nem string! Másik fontos dolog, hogy ide nem lehet feltételeket tenni, mint az if esetében.
  3. Az except ágba kerül az a kód, ami KeyError hiba esetén lefut majd.

Futtassuk így a programot, és írjuk be hogy: 2016-4-10. Így már a Hibás hónap! felirat jelenik meg a konzolban. Ez egyből sokkal barátságosabb.

Most próbáljuk meg a legelső hibás példa szerint begépelni a dátumot, azaz pontokkal elválasztva. Ekkor még mindig hibaüzenet kapunk, mivel ez nem KeyError típusú hiba volt.

A try-hoz tetszőleges számú except adható, így kezelhetjük a ValueError-t is:

try:
    print(pretty_date(today))
except KeyError:
    print('Hibás hónap!')
except ValueError:
    print('Hibás formátum!')

Innen már egyszerű: ha a try-ban KeyError történt akkor az az alatti ágban; ha ValueError keletkezett akkor meg annak az ágába folytatódik a program.

Lehetséges egy ágban lekezelni több különböző típusú hibát is a típusok felsorolásával:

try:
    print(pretty_date(today))
except (KeyError, ValueError):
    print('Hibás dátum!')

Bár úgy nézhet ki a zárójel miatt mintha az except egy függvény lenne, de az továbbra is utasítás mard. Az extra szóköz nyomatékosítja ezt.

A fenti kóddal általánosságban elmondhatjuk, hogy ha a pretty_date() akár KeyError akár ValueError hibát dob, akkor hibás volt a dátum amit begépelt a felhasználó.

Az else ág

Írjuk át a programot így:

try:
   text = pretty_date(today)
except (KeyError, ValueError):
    print('Hibás dátum!')

print('A dátum:', text)

A szándék az volt, hogy a try-ban csak a dátum konvertálását végezzük el. Ha ez sikeres volt, akkor a kiírás is biztos menni fog, így annak felesleges try-ban maradnia.

Ha helyes dátumot írunk be, akkor minden a számításaink szerint zajlik majd. De ha hibásan:

Hibás dátum!
Traceback (most recent call last):
  File "/home/slapec/.PyCharm2016.1/config/scratches/blog.py", line 33, in <module>
    print('A dátum:', text)
NameError: name 'text' is not defined

Akkor csak egy jóféle NameError exceptiont kapunk csak. Ez azért történt, mert a text változót a try ágban deklaráltuk. A hiba miatt viszont a deklaráció nem történt meg, így a try-on kívül nem létező változót próbáltunk meg kiírni.

Ezt többféleképp is elkerülhetjük, de talán a legjobb megoldás az, ha az else ágát használjuk:

try:
   text = pretty_date(today)
except (KeyError, ValueError):
    print('Hibás dátum!')
else:
    print('A dátum:', text)

Az else igazából az except-el van kapcsolatban: KeyError vagy ValueError esetén fusson az except ág, különben fusson az else ág. Tehát ebben az ágban van az a kód, amivel folytatni kell a programot ha nem volt hiba.

A finally ág

Az utolsó ág ami a try-hoz tartozik a finally. Az ebben álló kód mindenképp lefut amikor a program futása elhagyja a try blokkot, akár volt hiba, akár nem.

Ide általában cleanup kódot szoktak tenni, például bezárják a nyitott fájlokat, vagy kiírnak valami üzenetet mielőtt elhalálozik a program.

Mivel még nem tudjuk hogy hogyan kell fájlba írni, ezért innen most hiányzik a példa.

A nagyon nem várt hiba

A fenti algoritmust végülis egész jól körbebástyáztuk. A komplex programokban könnyen előfordul, hogy bár egy csomó esetre felkészültünk, valamiért mégis olyan hibát kapunk, amire aztán egyáltalán nem számítottunk.

A beépített hiba típusok hierarchikus elrendezésűek, azaz a hierarchia alján álló típusokra minden igaz ami a felettük álló típusokra (öröklik azok tulajdonságait). Ennek a hierarchiának a tetején az Exception típus áll. Azaz ha ezt a típusú hibát kapjuk el, akkor tényleg minden hibát le tudunk kezelni:

try:
    print(pretty_date(today))
except Exception:
    print('Hibás dátum!')

Opcionálisan így rövidíthető a fenti kód:

try:
    print(pretty_date(today))
except:
    print('Hibás dátum!')

Egyes esetekben elvárt, hogy a program semmilyen körülmények között se álljon le, viszont az ilyen hibakezeléssel azt kockáztatjuk, hogy az amúgy igen súlyos fennakadásokról sose kapunk majd tájékoztatást.

Saját hiba készítése

Nem csak a Pythontól várhatunk hibákat, hanem saját magunk is eldobhatunk exception-öket amiket valahol máshol elkaphatunk. Ez többek között függvényekben jöhet nagyon jól: így hibával jelezhetjük, ha nem megfelelő értékeket kaptunk.

Írjunk egy nagyon egyszerű kis függvényt, ami kizárólag pozitív számokat adhat össze:

def add(a, b):
    if a >= 0 and b >= 0:
        return a + b
    else:
        raise ValueError('A paramétereknek 0-nak vagy annál nagyobbnak kell lenniük')

Az 5. sorban látható az újdonság, a raise. Ezzel az utasítással egy exception objektumot dobhatunk el, magyarul így tudunk hibát létrehozni. Itt ugyan azt a ValueError-t használtam, mint amit korábban a hibás formátumú dátumnál elkaptunk.

A ValueError-t majdnem úgy kell használni mint egy függvényt, csak ez igazából egy osztály. Régóta kerülgetjük már az osztály fogalmát, pár rész és végre tényleg megdumáljuk hogy mik is azok valójában.

Ha megfelelő, tehát 0 vagy annál nagyobb argumentummal hívjuk meg a függvényt akkor minden rendben lesz. Ha valamelyik argumentum viszont negatív, például így:

add(-5, 10)

Akkor ezt a csinos hibaüzenetet kapjuk:

Traceback (most recent call last):
  File "/home/slapec/.PyCharm2016.1/config/scratches/blog.py", line 8, in <module>
    add(-5, 10)
  File "/home/slapec/.PyCharm2016.1/config/scratches/blog.py", line 5, in add
    raise ValueError('A paramétereknek 0-nak vagy annál nagyobbnak kell lenniük')
ValueError: A paramétereknek 0-nak vagy annál nagyobbnak kell lenniük

Őszintén megmondom, igen csinos ez a kis üzenet. Innentől már csak rajtunk áll, hogy hol-milyen hibákat akarunk eldobálni.

Természetesen nem csak a ValueError használható; itt van a korábban látott KeyError is, és még egy csomó más típust tartalmaz beépítve a Python, de a legjobb hír az, hogy később majd mi is fogunk teljesen saját hiba típusokat létrehozni.

Az üres utasítás

Levezetésnek következzen egy új utasítás, amiről idáig nem volt szó: a pass.

Pythonban nem hagyhatunk üresen blokkokat, de van amikor mégis erre lenne szükség: például hiba esetén egyszerűen ne csináljunk semmit. Ekkor használható a pass utasítás. Ez az utasítás a szó szoros értelmében nem csinál semmit, csak járattatja a processzort. Tölteléknek használható.

Vegyük elő újra a dátum beolvasós példát. Írjuk át úgy, hogy ha hiba történik, akkor szépen csöndben lépjünk ki minden felhajtás nélkül:

def get_month(month):
    months = {
        '01': 'január',
        '02': 'február',
        '04': 'március',
        '05': 'április',
        '06': 'június',
        '07': 'július',
        '08': 'augusztus',
        '09': 'szeptember',
        '10': 'október',
        '11': 'november',
        '12': 'december'
    }

    return months[month]

def pretty_date(today):
    parts = today.split('-')
    year, month, day = parts
    month_text = get_month(month)

    return '{0}. {1} {2}.'.format(year, month_text, day)


today = input('A mai dátum: ')

try:
    print(pretty_date(today))
except (KeyError, ValueError):
    pass

A legutolsó sorban van a lényeg. A pass akárhol használható egyébként (függvényben, ciklusban, stb) de csak akkor van értelme használni, ha önmagában áll, máskülönben akármilyen más utasítás behelyettesíthető a helyére.

Függvényben akkor szokták használni, ha a függvény neve már megvan, de az algoritmus még nincs. Ilyenkor hogy a függvény hívható legyen egy pass utasítást tesznek a törzsébe és később írják meg a kódot a helyére.

Utolsó példa

Írjuk át úgy a fenti kódot, hogy a program próbáljon meg újra dátumot beolvasni ha hiba történt.

while True:
    today = input('A mai dátum: ')

    try:
        print(pretty_date(today))
        break
    except (KeyError, ValueError):
        print('Hibás dátum!')

A végtelenségig várjuk a begépelt szöveget és majd a 6. sorban ugrunk ki a ciklusból. Az 5. sorban megpróbáljuk átalakítani a dátum átalakítását. Ha ezen a ponton hiba történik akkor a futás az except ágra ugrik. Itt nem állítjuk meg a ciklust, így az újra indul majd.

Ha viszont nem volt hiba, akkor a dátum kiírása után a program 6. sorral folytatódik, ahol a break utasítás kiléptet a ciklusból. Ilyen egyszerű.

Zárás

Azt hittem hogy ez a bejegyzés nem lesz bazi hosszú, és hogy még a modulokra is lesz idő. Ehhez képest már majdnem 20.000 karakternél járok, úgyhogy jobb is itt befejezni ezt a témát.

A következő részben tényleg jönnek a modulok, és remélhetőleg végre fájlba is fogunk írni.

Ez a bejegyzés a Python tutorialom egyik része. Az összes rész listája itt fellelhető.

-slp

Nincsenek megjegyzések:

Megjegyzés küldése