Змінна, визначена with-statement, доступна за межами with-block?


90

Розглянемо наступний приклад:

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

Я прочитав мовні документи (2.7) для with-statement, а також PEP-343, але, наскільки я можу сказати, вони нічого не говорять з цього приводу.

У CPython 2.6.5 f, здається, визначається поза блоком with, але я волію не покладатися на деталі реалізації, які можуть змінитися.


8
На питання про те, чи буде f доступним у межах, що вкладаються, вже дано відповіді. Для мене вся концепція менеджерів контексту клацнула , коли я зрозумів , що поняття контексту відрізняється , що від обсягу . Ось посилання на мій веб-сайт, яке, сподіваюся, трохи допоможе: markus-gattol.name/ws/python.html#context_manager
— Том

1
Точно - контекст - це питання зміни поточного стану - файл відкритий, файл закритий або нитка заблокована / розблокована. Пристрій виділено / звільнено. Всі змінні, названі в області дії, все ще є, але тепер вони вказуватимуть на деблоковані / закриті / розблоковані дескриптори.
— Danny Staple

Відповіді:


159

Так, менеджер контексту буде доступний поза оператором with, і це не залежить від реалізації чи версії. з операторами не створюють нову область виконання.


3
На мою думку, це найбільш чітке пояснення, тому присудження прийнятої відповіді; дасть бали Алексу та TokenMacGuy за додаткову корисну інформацію.
— Heikki Toivonen

Щось, про що можна легко забути, якщо якийсь час не працював з Python, така функція, як відступ, ім'я та інше, передбачає, що ви не зможете отримати до нього доступ, але все ж можете.
— Віталій Терзієв

28

withсинтаксис:

with foo as bar:
    baz()

приблизно становить цукор для:

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

Це часто корисно. Наприклад

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

with myLock:
    frob_some_more()

менеджер контексту може бути корисним більше одного разу.


Ну, повторне використання блокування може і не мати (не маю ідеї, але це було б помилкою, якби вони були різними) - але правила масштабування Python точно будуть однаковими тут у всіх реалізаціях.
— fuzzyman

1
це знову не питання обсягу. Область дії буде однаковою. Однак, якщо реалізація foo .__ exit__ переводить потік у зупинений стан, тоді, якщо lock не має введення, яке переблокує його, друге твердження не схоже на те, що це зробить щось корисне для блокування потоку.
— Danny Staple

16

Якщо fфайл є, він буде закритим поза withзаявою.

Наприклад, це

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

надрукував би:

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

Ви можете знайти деталі в PEP-0343 у розділі Специфікація: Заява «з» . Правила обсягу Python (що може дратувати ) стосуються fтакож.


Я це знаю, я згадував це у питанні. Для CPython 2.6.5 принаймні. Але чи можете ви гарантувати, що це те саме стосується Jython, IronPython та PyPy?
— Хейккі Тойвонен,

Правила обсягу Python теж не завжди є настільки чіткими. Розглянемо це в CPython 2.6.5: [x for x in [1]]. xдоступний поза цим. Зробити це в генератор: (x for x in [1]). Зараз xнедоступний. Здається, я пам’ятаю, що це було змінено в Python 3, щоб навіть при розумінні списку xне протікало, але я зараз не можу знайти посилання.
— Хейккі Тойвонен

Я шукав, але поки що не знайшов нічого суттєвого. Однак цікаве питання.
— miku

Насправді це не питання сфери дії - змінна f все ще доступна, але тепер це дескриптор для подання у закритому стані - того самого файлу, який раніше був відкритий раніше. Виклик виходу, коли контекст залишиться, змінить цей стан.
— Danny Staple

11

Щоб відповісти на запитання Хейккі в коментарях: так, така поведінка масштабування є частиною специфікації мови python і працюватиме на будь-якому сумісному Pythons (що включає PyPy, Jython та IronPython).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.