2017-05-16 39 views
9

Rozważ zastosowanie następującego fragmentu kodu Python 2.x.Plonowanie od wewnątrz instrukcją i __exit__ metoda menedżera kontekstu

from __future__ import print_function 


class myfile(file): 
    def __exit__(self, *excinfo): 
     print("__exit__ called") 
     super(myfile, self).__exit__(*excinfo) 


def my_generator(file_name): 
    with myfile(file_name) as fh: 
     for line in fh: 
      yield line.strip() 


gen = my_generator('file.txt') 
print(next(gen)) 
print("Before del") 
del gen 
print("After del") 

Wyjście z tego skryptu (podane plik.txt ma więcej niż jedną linię) jest:

Line 1 from file 
Before del 
__exit__ called 
After del 

Jestem zainteresowany o __exit__ rozmowy konkretnie.

Co powoduje uruchomienie jego metody? Z tego, co wiemy, kod nigdy nie opuścił instrukcji with ("zatrzymał się" po wydaniu yield i nigdy nie kontynuował). Czy jest zagwarantowane, że __exit__ zostanie wywołany, gdy liczba referencyjna generatora spadnie do 0?

+2

Drugie następne połączenie było pozostałością po nie-jeszcze-minimalnym przykładzie. Naprawiłem fragment kodu, teraz jest poprawny. –

+1

To pytanie dotyczy tylko CPython? – MSeifert

+0

Wydaje się, że jest tak: semantycznie jest to podobne do posiadania 'finally': https://www.python.org/dev/peps/pep-0343/ –

Odpowiedz

4

Na rekultywacji obiektu generatora wywołań Pythona jego metoda close, podnosząc wyjątek GeneratorExit w momencie jego ostatniego yield, jeśli nie został jeszcze zakończony. Ponieważ propaguje to GeneratorExit, wyzwala ona metodę __exit__ używanego menedżera kontekstów.

Zostało to wprowadzone w Pythonie 2.5, in the same PEP as send and yield expressions. Wcześniej nie można było yield wewnątrz try z finally, a jeśli instrukcje istniały pre-2.5, nie byłby w stanie yield wewnątrz jednego.

1

Aby dodać do @user2357112's answer, blok with zostanie zerwany, gdy zostanie zgłoszony wyjątek. Ten wyjątek jest przekazywany do metody obiektu utworzonej dla kontekstu.

Klasa file wydaje się w milczeniu pominąć wyjątek GeneratorExit, ponieważ nic nie sygnalizuje. Jednakże, jeśli wydrukować argc w swojej metodzie myfile.__exit__, widać, że kontekst nie został zamknięty naturalnie:

class myfile(file): 
    def __exit__(self, *excinfo): 
     print("__exit__ called") 
     print(excinfo[0]) # Print the reason why the context exited 
     super(myfile, self).__exit__(*excinfo) 

Wyjście skryptu:

Line 1 from file 
Before del 
__exit__ called 
<type 'exceptions.GeneratorExit'> 
After del