Переменная, определенная оператором with, доступна вне блока with?

90

Рассмотрим следующий пример:

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

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

В CPython 2.6.5 fкажется, что он определен вне блока with, но я бы предпочел не полагаться на детали реализации, которые могут измениться.

Хейкки Тойвонен
источник
8
На вопрос о том, будет ли f доступна в прилагаемой области видимости, уже был дан ответ. Для меня вся концепция контекстных менеджеров щелкнула, когда я понял, что концепция контекста отличается от концепции области действия . Вот ссылка на мой веб-сайт, которая, надеюсь, немного поможет: markus-gattol.name/ws/python.html#context_manager
Tom
1
Точно - контекст - это вопрос изменения текущего состояния - файл открыт, файл закрыт или поток заблокирован / разблокирован. Устройство выделено / освобождено. Все переменные, указанные в области видимости, все еще там, но теперь они будут указывать на дескрипторы освобожденные / закрытые / разблокированные.
Дэнни Стейпл

Ответы:

160

Да, диспетчер контекста будет доступен вне оператора with, и он не зависит от реализации или версии. Операторы with не создают новую область выполнения.

пушистик
источник
3
На мой взгляд, это наиболее ясное объяснение, поэтому мы должны признать принятый ответ; даст баллы Alex и 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()

диспетчер контекста может быть использован более одного раза.

SingleNegationElimination
источник
Что ж, повторное использование блокировки может или не может (не знаю, но это было бы ошибкой, если бы они были разными), но правила области видимости Python определенно будут одинаковыми здесь во всех реализациях.
fuzzyman
1
это опять же не вопрос объема. Объем будет таким же. Однако, если реализация foo .__ exit__ переводит поток в остановленное состояние, тогда, если lock не имеет входа, который повторно блокирует его, второй оператор не выглядит так, как будто он сделал бы что-нибудь полезное для блокировок потока.
Дэнни Стейпл
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?
Heikki Toivonen
Правила области видимости Python тоже не всегда так ясны. Рассмотрим это в CPython 2.6.5: [x for x in [1]]. xдоступен за пределами этого. Сделать это в генератор: (x for x in [1]). Сейчас xнет в наличии. Я, кажется, припоминаю, что это было изменено в Python 3, так что даже при понимании списка xне будет утечки, но сейчас я не могу найти ссылку.
Heikki Toivonen
Я искал, но пока не нашел ничего существенного. Но интересный вопрос.
miku
На самом деле это не вопрос области видимости - переменная f все еще доступна, но теперь это дескриптор файла в закрытом состоянии - того же файла, который ранее был открыт. Вызов выхода при выходе из контекста изменит это состояние.
Дэнни Стейпл
11

Чтобы ответить на вопрос Хейкки в комментариях: да, это поведение области видимости является частью спецификации языка python и будет работать на всех без исключения совместимых Python (включая PyPy, Jython и IronPython).

Алекс Гейнор
источник