C# týden C# týden
Pořádné programy s pořádnou klávesnicí zdarma. Více zde
Pouze tento týden sleva až 80 % na C# .NET

Lekce 1 - Výjimky v Pythonu

Python Práce se soubory Výjimky v Pythonu

ONEbit hosting Unicorn College Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V tomto Python kurzu se budeme věnovat práci se soubory. Než však můžeme začít zapisovat a číst, měli bychom vyřešit, jak ošetřit chybové stavy programu, kterých při práci se soubory bude nastávat mnoho.

V našem programu může často dojít k chybě. Tím nemyslím chybě z důvodu, že byl program funkčně špatně napsaný, takových chyb se jsme schopni dobře vyvarovat. Obecně se jedná zejména o chyby, které zapříčinily tzv. vstupně/výstupní operace. V anglické literatuře se hovoří o input/output nebo zkráceně o IO. Jedná se např. o vstup uživatele z konzole, ze souboru, výstup do souboru, na tiskárnu a podobně. V zásadě platí, že zde figuruje uživatel, který nám může zadat nesmyslný vstup, neexistující nebo nevalidní soubor, odpojit tiskárnu a podobně. My však nenecháme program spadnout s chybou, naopak budeme zranitelná místa v programu ošetřovat a na danou skutečnost uživatele upozorníme.

Aktivní ošetření chyb

První možnost ošetření chyb nazýváme jako aktivní. V programu zmapujeme všechna zranitelná místa a ošetříme je podmínkami. Jako učebnicový příklad se zpravidla používá dělení nulou. Představme si program, který používá třídu Matematika, která má metodu podil(). Třída by mohla vypadat např. takto:

class Matematika():

    def podil(self, a, b):
        return a / b

Nyní třídu použijeme takovýmto způsobem:

print("Zadejte dělitele a dělence k výpočtu podílu:")
a = int(input())
b = int(input())
print(Matematika().podil(a, b))

Pokud nyní programu uživatel zadá čísla 12 a 0, program spadne s chybou, protože nulou nelze dělit. Aktivně chybu ošetříme jednoduchou podmínkou v programu:

print("Zadejte dělitele a dělence k výpočtu podílu:")
a = int(input())
b = int(input())
if b != 0:
    print(Matematika().podil(a, b))
else:
    print("Nulou nelze dělit.")

Nyní si musíme při každém použití metody tedy hlídat, jestli do druhého parametru nevkládáme nulu. Představte si, že by metoda brala parametrů 10 a používali jsme ji v programu několikrát. Určitě by bylo velmi složité ošetřovat všechna použití této metody.

Řešením by mohlo být vložit kontrolu přímo do metody. Máme tu však nový problém: Jakou hodnotu vrátíme, když bude 2. parametr nulový? Potřebujeme hodnotu, ze které poznáme, že výpočet neproběhl korektně. To je však problém, když zvolíme např. nulu, nepoznáme, zda např. 0/12 je chybný výpočet či nikoli. Nevíme, zda 0 značí výsledek nebo chybu. Ani zápornými čísly si nepomůžeme. Parsování hodnot je 2. klasický příklad zranitelného vstupu od uživatele. Další jsou souborové operace, kde soubor nemusí existovat, nemusíme na něj mít práva, může s ním být zrovna pracováno a podobně.

Pasivní ošetření chyb

Zejména, když je operace složitější a bylo by příliš náročné ošetřovat všechny možné chybové stavy, nastupují výjimky, tzv. pasivní ošetření chyb. Nás totiž vůbec nemusí zajímat vnitřní logika v metodě, kterou voláme. Pokusíme se nebezpečnou část kódu spustit v "chráněném režimu". Tento režim je nepatrně pomalejší a liší se tím, že pokud dojde k chybě, máme možnost ji odchytit a zabránit pádu programu. O chybě zde hovoříme jako o výjimce. Využíváme k tomu tzv. try-except bloky:

try:
    pass
except:
    pass

Do bloku try umístíme nebezpečnou část kódu. Pokud nastane v bloku try chyba, jeho vykonávání se přeruší a program přejde do bloku except. Pokud vše proběhne v pořádku, try se vykoná celý a except se přeskočí. Vyzkoušejme si situaci na našem předchozím příkladu:

try:
    print(Matematika().podil(a, b))
except:
    print("Při dělení nastala chyba.")

Kód je jednodušší v tom, že nemusíme ošetřovat všechna zranitelná místa a přemýšlet, co vše by se mohlo pokazit. Nebezpečný kód pouze obalíme blokem try a všechny chyby se zachytí v except. Samozřejmě do try-except bloku umístíme jen to nezbytně nutné, ne celý program :)

Nyní tedy již víme, jak ošetřit situace, kdy uživatel zadává nějaký vstup, který by mohl vyvolat chybu. Nemusí se jednat jen o souborové operace, výjimky mají velmi širokou oblast použití. Dokážeme náš program napsat tak, aby se nedal jednoduše uživatelem shodit.

Použití výjimek při práci se soubory

Jak již bylo řečeno, souborové operace mohou vyvolat mnoho výjimek, proto se soubory vždy pracujeme v try-except bloku. Existuje také několik dalších konstrukcí, které při výjimkách můžeme využívat.

Finally

Do try-except bloku můžeme přidat ještě 3. blok a to finally. Ten se spustí vždy ať k výjimce došlo či nikoli. Představte si následující metodu pro uložení nastavení:

def ulozNastaveni():
    try:
        f = open("soubor.dat", "w")
        f.write("Nějaké nastavení...")
        f.close() # musíme file handler uzavřít
    except:
        print("Chyba při zápisu do souboru.")

Metoda se soubor pokusí otevřít a zapsat do něj nějaké nastavení. Otevřený soubor musíme opět uzavřít. Při chybě vypíše hlášku do konzole. Vypisovat chyby přímo v metodě je však ošklivé, to ostatně již víme, metody a objekty obecně by měly provádět jen logiku a komunikaci s uživatelem obstarává ten, kdo je volá. Vracejme tedy True/False podle toho, zda se operace povedla či nikoli:

def ulozNastaveni():
    try:
        f = open("soubor.dat", "w")
        f.write("Nějaké nastavení...")
        return True
    except:
        return False
    f.close()

Na první pohled to vypadá, že se soubor vždy uzavře. Celý kód je však v nějaké metodě, ve které voláme return. Jak víme, return ukončí metodu a nic za ním se již neprovede.

Soubor by zde vždy zůstal otevřený a uzavření by se již neprovedlo. Jako následek by to mohlo mít, že by byl poté soubor nepřístupný. Pokud vložíme zavření souboru do bloku finally, vykoná se vždy. Python si pamatuje, že blok try-except obsahoval finally a zavolá finally blok i po opuštění bloku except nebo try:

def ulozNastaveni():
    try:
        f = open("soubor.dat", "w")
        f.write("Nějaké nastavení...")
        return True
    except:
        return False
    finally:
        f.close()

Blok except by bylo nejlepší úplně vynechat a nechat metodu, aby výjimku klidně vyvolala. Budeme počítat s tím, že se s výjimkou vypořádá ten, kdo metodu zavolal, nikoli metoda sama. Je to tak lepší, ušetříme návratovou hodnotu metody (kterou lze poté použít pro něco jiného) a kód se nám zjednoduší:

def ulozNastaveni():
    try:
        f = open("soubor.dat", "w")
        f.write("Nějaké nastavení...")
    finally:
        f.close()
}

Soubor se v kódu výše vždy zavře a to i když se při zápisu něco nepovede, třeba dojde místo na médiu. Metodu bychom nyní volali takto:

try:
    ulozNastaveni()
except:
    print("Nepodařilo se uložit nastavení.")

Nyní si ukážeme, jak celou situaci ještě více zjednodušit. Použijeme konstrukci with.

With

Python umožňuje značně zjednodušit práci s instancemi tříd ke čtení a zápisu do souborů nebo obecně tříd, které potřebují provádět jakékoli úklidové práce. Výše uvedený blok můžeme zapsat pomocí notace with, která nahrazuje bloky try a finally. Obrovskou výhodou je, že blok finally Python vygeneruje sám a sám zajistí, aby daná instance soubor uzavřela. Metoda UlozNastaveni() by tedy vypadala s pomocí with takto:

def ulozNastaveni():
    with open("soubor.dat", "w") as f:
        f.write(objekt)
}

Vidíme, že se kód extrémně zjednodušil, i když dělá v podstatě to samé. Při volání metody opět použijeme try-except blok.

Nezapomeňte, že with nahrazuje pouze try-finally, nikoli except!. Metodu, ve které se použivá with, musíme stejně volat v try-except bloku.

Nyní jsme dospěli přesně tam, kam jsem chtěl. K veškerým manipulacím se soubory totiž budeme v následujících tutoriálech používat konstrukci with. Kód bude jednodušší a nikdy se nám nestane, že bychom soubor zapomněli zavřít.

K výjimkám se ještě jednou vrátíme, ukážeme si, jak odchytávat jen některé typy výjimek, které hotové třídy výjimek můžeme v našich programech používat a také, jak vytvořit výjimku vlastní. Teď jsem však chtěl vysvětlit jen potřebné minimum pro práci se soubory a ne vám zbytečně plést hlavu složitými konstrukcemi :)

V příští lekci, Úvod do práce se soubory v Pythonu, se podíváme, jak to funguje s právy k zápisu do souborů v systému Windows a vyzkoušíme si několik prvních souborových operací.


 

 

Článek pro vás napsal MQ .
Avatar
Jak se ti líbí článek?
Ještě nikdo nehodnotil, buď první!
Autor je srdcem Pythonista.
Miniatura
Všechny články v sekci
Práce se soubory v Pythonu
Miniatura
Následující článek
Úvod do práce se soubory v Pythonu
Aktivity (2)

 

 

Komentáře

Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zatím nikdo nevložil komentář - buď první!