Jest dokładnie jeden powód, dla którego preferowane jest to:
with open('filename.txt') as fp:
for line in fp:
print line
Wszyscy jesteśmy rozpieszczani przez stosunkowo deterministyczny schemat liczenia odniesień w CPythonie do czyszczenia pamięci. Inne, hipotetyczne implementacje Pythona niekoniecznie będą musiały zamknąć plik „wystarczająco szybko” bez withbloku, jeśli używają innego schematu odzyskiwania pamięci.
W takiej implementacji może pojawić się błąd „zbyt wiele otwartych plików” z systemu operacyjnego, jeśli kod otwiera pliki szybciej niż moduł odśmiecania wywołań finalizatorów na uchwytach osieroconych plików. Zwykłym obejściem jest natychmiastowe wyzwolenie GC, ale jest to paskudny hack i musi to być wykonane przez każdą funkcję, która może napotkać błąd, w tym te w bibliotekach. Co za koszmar.
Lub możesz po prostu użyć withbloku.
Pytanie dodatkowe
(Przestań czytać teraz, jeśli interesują Cię tylko obiektywne aspekty pytania).
Dlaczego nie jest to uwzględnione w protokole iteratora dla obiektów plików?
To jest subiektywne pytanie dotyczące projektowania API, więc mam subiektywną odpowiedź w dwóch częściach.
Na poziomie intuicji wydaje się to złe, ponieważ sprawia, że protokół iteratora wykonuje dwie oddzielne rzeczy - iteruje po liniach i zamyka uchwyt pliku - i często jest złym pomysłem, aby prosta funkcja wykonywała dwie czynności. W tym przypadku jest to szczególnie złe, ponieważ iteratory odnoszą się w quasi-funkcjonalny, oparty na wartościach sposób do zawartości pliku, ale zarządzanie uchwytami plików to zupełnie osobne zadanie. Zgniecenie obu, niewidocznie, w jedną akcję, jest zaskakujące dla ludzi, którzy czytają kod i utrudnia wnioskowanie o zachowaniu programu.
Inne języki zasadniczo doszły do tego samego wniosku. Haskell krótko flirtował z tak zwanym „leniwym IO”, który pozwala na iterację pliku i automatyczne zamknięcie go, gdy dojdziesz do końca strumienia, ale obecnie prawie zawsze odradza się używanie leniwego IO w Haskell i Haskell użytkownicy przeważnie przeszli do bardziej jawnego zarządzania zasobami, takiego jak Conduit, który zachowuje się bardziej jak withblok w Pythonie.
Na poziomie technicznym jest kilka rzeczy, które możesz chcieć zrobić z uchwytem pliku w Pythonie, który nie działałby tak dobrze, gdyby iteracja zamknęła uchwyt pliku. Na przykład załóżmy, że muszę dwukrotnie powtórzyć ten plik:
with open('filename.txt') as fp:
for line in fp:
...
fp.seek(0)
for line in fp:
...
Chociaż jest to mniej powszechny przypadek użycia, weź pod uwagę fakt, że mogłem właśnie dodać trzy wiersze kodu na dole do istniejącej bazy kodu, która pierwotnie miała trzy górne wiersze. Gdyby iteracja zamknęła plik, nie byłbym w stanie tego zrobić. Tak więc oddzielenie iteracji i zarządzania zasobami ułatwia komponowanie fragmentów kodu w większy, działający program w języku Python.
Kompatybilność jest jedną z najważniejszych cech użyteczności języka lub API.