Ještě jednou a lépe: práce se souborem v Pythonu

Od Petr Zemek, 2013-11-17

Často je potřeba provést se souborem nějakou činnost, např. načíst celý obsah souboru do paměti. Jaká je typická podoba kódu, který pro tento účel vídávám u začínajících programátorů v Pythonu a jak to udělat lépe se dozvíte v dnešním příspěvku.

Původní kód

V příspěvku se pro jednoduchost zaměřím pouze na čtení obsahu celého souboru do paměti. Avšak stejný princip, jako ukáži níže, platí samozřejmě pro další operace.

Vezměme si následující kód:

f = open('file.txt')
contents = f.read()
f.close()

Soubor otevřeme, načteme z něj jeho obsah a uzavřeme jej.

Proč takto raději ne?

Co když kód f.read() způsobí vyhození výjimky? Pak nedojde k zavolání f.close() a soubor se uzavře, až když bude objekt f odstraněn garbage collectorem (toto samozřejmě závisí na implementaci Pythonu). Lze se tomu vyhnout použitím bloku try/finally:

f = open('file.txt')
try:
    contents = f.read()
finally:
    f.close()

Stále je zde však ono volání f.close(), na které bychom neměli zapomenout, což se může snadno stát. Navíc je kód méně přehledný, než původní verze.

Jak to udělat lépe?

Od Pythonu 2.5 máme příkaz with, který se k tomuto úkolu perfektně hodí. Tento příkaz je samozřejmě k dispozici i v Pythonu 3. Kód s jeho využitím bude vypadat následovně:

with open('file.txt') as f:
    contents = f.read()

Nejdříve se otevře soubor file.txt. Pokud se otevření podaří, tak dojde k pokusu o načtení dat. Ať už ono načtení uspěje či neuspěje, tak se otevřený soubor automaticky uzavře. Čili to funguje tak, jako ukázka s try/finally výše, ale kód je mnohem přehlednější a kratší.

Další čtení

Níže přidávám pár odkazů pro zájemce o další informace.

Obsah tohoto pole je soukromý a nebude veřejně zobrazen.

Filtrované HTML (využíváno)

  • Povolené HTML značky: <a href hreflang> <em> <strong> <cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <table>
  • Zvýraznění syntaxe kódu lze povolit přes následující značky: <code>, <blockcode>, <bash>, <c>, <cpp>, <haskell>, <html>, <java>, <javascript>, <latex>, <perl>, <php>, <python>, <ruby>, <rust>, <sql>, <text>, <vim>, <xml>, <yaml>.
  • Řádky a odstavce se zalomí automaticky.
  • Webové a e-mailové adresy jsou automaticky převedeny na odkazy.
CAPTCHA
9 + 8 =
Vyřešte tento jednoduchý matematický příklad a vložte výsledek. Např. pro 1+3 vložte 4.
Nějak se mi tady rozmohl spam, takže poprosím o ověření.

JirkaK (neověřeno)

10 years 10 months zpět

Ahoj Petře,

ještě bych se zmínil o variantě příkazu with, která povoluje mít za klíčovým slovem with více kontextových výrazů, vzájemně od sebe oddělených čárkou. Této konstrukce lze využít k zachytávání výjimek vyvolaných uvnitř těla příkazu with, což může být užitečné, chceme-li například informovat uživatele o (ne)úspěchu dané operace. Příklad:

class ExceptionHandler:
    def __init__(self):
        self.type = None
        self.value = None
        self.traceback = None
    def __enter__(self):
        return self
    def __exit__(self, type, value, traceback):
        self.type = type
        self.value = value
        self.traceback = traceback
        return True # Důležité! V případě False by došlo k opětovnému vyvolání výjimky
 
eh = ExceptionHandler()
with eh, open("a.txt") as f:
    s = f.read()
if eh.value is not None:
    print(eh.value)

Zde, pokud operace open nebo read selžou, tak vyvolají výjimku, a informace o této výjimce jsou následně uloženy do eh, z níž si je lze získat (samozřejmě vše, co bylo o with popsáno v článku, platí i v tomto případě).

Jirka

PS: Pokud by jsi měl nějaké elegantnější řešení, jak se s daným problémem vypořádat, tak ho klidně uveď ;)