Używanie instrukcji Pythona „with” z blokiem try-except

99

Czy to właściwy sposób używania instrukcji Pythona „with” w połączeniu z blokiem try-except ?:

try:
    with open("file", "r") as f:
        line = f.readline()
except IOError:
    <whatever>

Jeśli tak, to rozważ stary sposób robienia rzeczy:

try:
    f = open("file", "r")
    line = f.readline()
except IOError:
    <whatever>
finally:
    f.close()

Czy główną zaletą instrukcji „with” jest to, że możemy pozbyć się trzech wierszy kodu? Nie wydaje mi się to zbyt ważne w tym przypadku użycia (chociaż rozumiem, że instrukcja „with” ma inne zastosowania).

EDYCJA: Czy funkcjonalność powyższych dwóch bloków kodu jest identyczna?

EDYCJA2: Kilka pierwszych odpowiedzi mówi ogólnie o korzyściach wynikających z używania „z”, ale wydaje się, że mają one marginalne korzyści. Od lat wszyscy (lub powinniśmy) otwarcie wywoływać funkcję f.close (). Przypuszczam, że jedną z korzyści jest to, że niechlujni programiści skorzystają na używaniu „z”.

gaefan
źródło
Dla mnie brak konieczności pamiętania o zamykaniu () rzeczy w końcowej instrukcji jest wystarczającym powodem, aby użyć słowa „z”. Widziałem wiele kodów, które nie zamykały swoich zasobów. A „z” nie ma wad, o ile widzę.
Raúl Salinas-Monteagudo

Odpowiedzi:

143
  1. Dwa bloki kodu, które podałeś, nie są równoważne
  2. Kod, który opisałeś jako stary sposób robienia rzeczy, ma poważny błąd: w przypadku niepowodzenia otwarcia pliku otrzymasz drugi wyjątek w finallyklauzuli, ponieważ fnie jest powiązany.

Odpowiedni kod w starym stylu wyglądałby tak:

try:
    f = open("file", "r")
    try:
        line = f.readline()
    finally:
        f.close()
except IOError:
    <whatever>

Jak widać, withstwierdzenie to może zmniejszyć podatność na błędy. W nowszych wersjach Pythona (2.7, 3.1) można również łączyć wiele wyrażeń w jednej withinstrukcji. Na przykład:

with open("input", "r") as inp, open("output", "w") as out:
    out.write(inp.read())

Poza tym osobiście uważam, że jak najwcześniejsze wyłapywanie wyjątków jest złym nawykiem. To nie jest celem wyjątków. Jeśli funkcja IO, która może zawieść, jest częścią bardziej skomplikowanej operacji, w większości przypadków IOError powinien przerwać całą operację i dlatego powinien być obsługiwany na zewnętrznym poziomie. Używając withinstrukcji, możesz pozbyć się wszystkich tych try...finallyinstrukcji na wewnętrznych poziomach.

Bernd Petersohn
źródło
7

Jeśli zawartość finallybloku jest określona przez właściwości otwieranego obiektu pliku, dlaczego implementujący obiekt pliku nie miałby zapisywać finallybloku? To zaleta withinstrukcji, znacznie większa niż zapisanie trzech wierszy kodu w tym konkretnym przypadku.

I tak, sposób, w jaki połączyłeś withi try-exceptjest właściwie jedynym sposobem na zrobienie tego, ponieważ wyjątkowych błędów spowodowanych w samym openoświadczeniu nie można złapać w withbloku.

Peter Milley
źródło
1

Myślę, że źle zrozumiałeś stwierdzenie „z”, że zmniejsza ono tylko linie. W rzeczywistości wykonuje inicjalizację i obsługuje porzucanie.

W twoim przypadku „z” robi

  • otworzyć plik,
  • przetworzyć jego zawartość i
  • pamiętaj, aby go zamknąć.

Oto link do zrozumienia instrukcji „with”: http://effbot.org/zone/python-with-statement.htm

Edycja: Tak, twoje użycie "z" jest poprawne, a funkcjonalność obu bloków kodu jest identyczna. Pytanie o to, dlaczego używać słowa „z”? dzieje się tak ze względu na korzyści, jakie z nim uzyskasz. jak wspomniałeś o przypadkowym braku f.close ().

YoK
źródło
-4

Bardziej Pythonowy sposób na następujące kody to:

try:
    f = open("file", "r")
    try:
        line = f.readline()
    finally:
        f.close()
except IOError:
    <whatever>

try:
    f = open("file", "r")
except IOError:
    <whatever>
else:
    f.close()
Leo Liu
źródło
1
Dodałem dla Ciebie formatowanie kodu; ułatwia czytanie. Ale możesz sprawdzić dwukrotnie, aby upewnić się, że nie złamałem wcięcia.
andrewsi
2
Nie, Twoja wersja nie działa tak samo jak oryginalny kod. Nawet jeśli dodasz brakujące readline()wywołanie, Twoja wersja nie zamknie pliku, jeśli readline()w wyniku IOError.
Aleksi Torhamo