W czasach prehistorycznych (Python 1.4) zrobiliśmy:
fp = open('filename.txt')
while 1:
line = fp.readline()
if not line:
break
print line
po Pythonie 2.1 zrobiliśmy:
for line in open('filename.txt').xreadlines():
print line
zanim otrzymaliśmy wygodny protokół iteratora w Pythonie 2.3 i mogliśmy zrobić:
for line in open('filename.txt'):
print line
Widziałem kilka przykładów z bardziej szczegółowym:
with open('filename.txt') as fp:
for line in fp:
print line
czy jest to preferowana metoda na przyszłość?
[edytuj] Rozumiem, że instrukcja with zapewnia zamknięcie pliku ... ale dlaczego nie jest to zawarte w protokole iteratora dla obiektów plikowych?
python
python-3.x
python-2.7
thebjorn
źródło
źródło
Odpowiedzi:
Jest dokładnie jeden powód, dla którego preferowane jest to:
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
with
bloku, 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ć
with
bloku.Pytanie dodatkowe
(Przestań czytać teraz, jeśli interesują Cię tylko obiektywne aspekty pytania).
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
with
blok 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:
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.
źródło
with
zapewnia jednak spokój ducha, więc nadal jest to najlepsza praktyka.Tak,
jest droga do zrobienia.
Nie jest bardziej szczegółowe. To jest bezpieczniejsze.
źródło
jeśli jesteś wyłączony przez dodatkową linię, możesz użyć funkcji opakowującej, takiej jak ta:
w Pythonie 3.3
yield from
instrukcja sprawiłaby, że jest to jeszcze krótsze:źródło
źródło