Zmienna zdefiniowana za pomocą instrukcji with dostępna poza with-block?

90

Rozważmy następujący przykład:

with open('a.txt') as f:
    pass
# Is f supposed to be defined here?

Przeczytałem dokumentację językową (2.7) dotyczącą instrukcji with oraz PEP-343, ale o ile wiem, nie mówią nic na ten temat.

W CPythonie 2.6.5 fwydaje się być zdefiniowany poza with-block, ale wolałbym nie polegać na szczegółach implementacji, które mogą się zmienić.

Heikki Toivonen
źródło
8
Odpowiedź na pytanie, czy f będzie dostępna w załączającym zakresie, została już udzielona. Dla mnie cała koncepcja menedżerów kontekstu kliknęła, gdy zdałem sobie sprawę, że koncepcja kontekstu różni się od zakresu . Oto link do mojej witryny, która, mam nadzieję, trochę pomoże: markus-gattol.name/ws/python.html#context_manager
Tom
1
Dokładnie - kontekst to kwestia zmiany aktualnego stanu - plik otwarty, plik zamknięty lub wątek zablokowany / odblokowany. Urządzenie zostało przydzielone / zwolnione. Wszystkie zmienne wymienione w zakresie nadal tam są - ale będą teraz wskazywać na uchwyty zwolnione / zamknięte / odblokowane.
Danny Staple,

Odpowiedzi:

159

Tak, menedżer kontekstu będzie dostępny poza instrukcją with i nie jest to zależne od implementacji ani wersji. z instrukcjami nie tworzą nowego zakresu wykonania.

fuzzyman
źródło
3
Moim zdaniem jest to najjaśniejsze wyjaśnienie, udzielając zaakceptowanej odpowiedzi; przyzna punkty Alexowi i TokenMacGuy, aby uzyskać dodatkowe pomocne informacje.
Heikki Toivonen
Coś, o czym można by łatwo zapomnieć, gdybyś nie pracował z Pythonem przez jakiś czas, funkcja taka jak wcięcia, nazwa i inne sugeruje, że nie powinieneś mieć do niego dostępu, a jednak możesz.
Vitaliy Terziev
28

withskładnia:

with foo as bar:
    baz()

to w przybliżeniu cukier dla:

try:
    bar = foo.__enter__()
    baz()
finally:
    if foo.__exit__(*sys.exc_info()) and sys.exc_info():
        raise

Jest to często przydatne. Na przykład

import threading
with threading.Lock() as myLock:
    frob()

with myLock:
    frob_some_more()

Menedżer kontekstu może być używany więcej niż raz.

SingleNegationElimination
źródło
Cóż, ponowne użycie blokady może lub nie (nie mam pojęcia, ale byłby to błąd, gdyby były różne) - ale zasady określania zakresu Pythona z pewnością będą takie same we wszystkich implementacjach.
fuzzyman
1
to znowu nie jest kwestia zakresu. Zakres będzie taki sam. Jednakże, jeśli implementacja foo .__ exit__ ustawia wątek w stan zatrzymania, to chyba, że ​​lock ma enter, który go ponownie blokuje, druga instrukcja nie wygląda tak, jakby robiłaby cokolwiek użytecznego dla blokad wątku.
Danny Staple
16

W przypadku, gdy fjest to plik, zostanie on zamknięty poza withwyciągiem.

Na przykład this

f = 42
print f
with open('6432134.py') as f:
    print f
print f

wydrukowałby:

42
<open file '6432134.py', mode 'r' at 0x10050fb70>
<closed file '6432134.py', mode 'r' at 0x10050fb70>

Szczegóły można znaleźć w PEP-0343 w sekcji Specyfikacja: Oświadczenie „z” . Zasady zakresu Pythona (co może być irytujące ) również mają zastosowanie f.

miku
źródło
Wiem o tym, wspomniałem o tym w pytaniu. Przynajmniej dla CPythona 2.6.5. Ale czy możesz zagwarantować, że to samo dotyczy Jython, IronPython i PyPy?
Heikki Toivonen
Zasady zakresu Pythona również nie zawsze są tak jasne. Rozważmy to na CPython 2.6.5: [x for x in [1]]. xjest dostępny poza tym. Uczynić go generatora: (x for x in [1]). Teraz xnie jest dostępny. Wydaje mi się, że zostało to zmienione w Pythonie 3, więc nawet przy zrozumieniu listy xnie wycieka, ale nie mogę teraz znaleźć odniesienia.
Heikki Toivonen
Szukałem, ale na razie nie znalazłem nic znaczącego. Ciekawe pytanie.
miku
W rzeczywistości nie jest to kwestia zakresu - zmienna f jest nadal dostępna, ale teraz jest uchwytem do pliku w stanie zamkniętym - tym samym pliku, który był wcześniej otwarty. Wywołanie wyjścia po opuszczeniu kontekstu zmieni ten stan.
Danny Staple
11

Aby odpowiedzieć na pytanie Heikkiego w komentarzach: tak, to zachowanie zakresu jest częścią specyfikacji języka Python i będzie działać na wszystkich zgodnych Pythonach (w tym PyPy, Jython i IronPython).

Alex Gaynor
źródło