Dane trochę technicznych, więc zacznijmy od prostej wersji:
Niektóre wiedzą, jak stosować w with
oświadczeniu. Obiekty plików, takie jak to, co otrzymałeś z numeru open
, są przykładem takiego typu. Jak się okazuje, obiekty, które otrzymasz od urllib.request.urlopen
, są również przykładem takiego typu, więc twój drugi przykład może być napisany tak samo jak pierwszy.
Ale niektóre typy nie wiedzą, jak używać w oświadczeniu with
.Funkcja closing
jest przeznaczona do owijania takich typów - o ile mają one metodę close
, wywoła ona ich metodę close
po zakończeniu instrukcji with
.
Oczywiście niektóre typy nie wiem, w jaki sposób mają być wykorzystywane w with
oświadczenia, a także nie może być używany z closing
ponieważ ich metoda oczyszczania nie jest nazwany close
(lub ponieważ ich czyszczenie jest bardziej skomplikowana niż tylko ich zamknięcie). W takim przypadku musisz napisać niestandardowy menedżer kontekstów. Ale nawet to nie jest zazwyczaj takie trudne.
W kategoriach technicznych:
with
stwierdzenie wymaga context manager, obiekt z __enter__
i __exit__
metod. Wywołuje metodę __enter__
i podaje wartość zwróconą przez tę metodę w klauzuli as
, a następnie wywoła metodę __exit__
na końcu instrukcji with
.
obiekty dziedziczą z io.IOBase
pliku, który jest menedżerem kontekst, którego metoda __enter__
zwraca się, a którego __exit__
rozmowy self.close()
.
Obiekt zwrócony przez urlopen
jest (zakładając http
lub https
URL) jest HTTPResponse
, które, jak docs powiedzieć, „może być używany wraz z oświadczeniem with
”.
closing
funkcja:
Powrót kierownik kontekst, który zamyka rzeczy po zakończeniu bloku. Jest to w zasadzie równoważne:
@contextmanager
def closing(thing):
try:
yield thing
finally:
thing.close()
To nie zawsze w 100% jasne w docs, które typy są menedżerowie kontekst i jakie typy nie są. Zwłaszcza, że od czasu wydania 3.1 nastąpiła poważna zmiana, aby uczynić wszystko, co może być menedżerem kontekstu, jednym (i, o ile to możliwe, uczynić wszystko, co jest w większości podobne do pliku, rzeczywistą wersją IOBase
), ale nadal nie jest to możliwe. 100% ukończone od 3.4.
Zawsze możesz po prostu spróbować i zobaczyć. Jeśli otrzymasz numer AttributeError: __exit__
, obiekt nie będzie można użyć jako menedżera kontekstu. Jeśli uważasz, że tak, należy zgłosić błąd sugerujący zmianę. Jeśli nie dostaniesz tego błędu, ale dokumenty nie wspominają, że jest to zgodne z prawem, zgłoś błąd sugerujący aktualizację dokumentów.
Chciałem zasugerować, że ktoś powinien zgłosić błąd do dokumentacji na 'contextlib', a jeśli to nie ty, ktokolwiek to powinien przyznać ... ale zanim skończyłem, Martijn już zgłosił błąd, z linkiem wróć tutaj. :) – abarnert