Python вложенные области с динамическими функциями

Нужна помощь в понимании следующего предложения из PEP 227 и Python Language Reference

Если на переменную ссылаются в закрытой области видимости, удаление имени является ошибкой. Компилятор вызовет SyntaxError для "del name".

Отсутствие примеров привело к тому, что я не смог воспроизвести ошибку во время компиляции, поэтому объяснение с примерами крайне желательно.

2 ответа

Решение

Следующее поднимает исключение:

def foo():
    spam = 'eggs'
    def bar():
        print spam
    del spam

поскольку spam переменная используется в закрытой области видимости bar:

>>> def foo():
...     spam = 'eggs'
...     def bar():
...         print spam
...     del spam
... 
SyntaxError: can not delete variable 'spam' referenced in nested scope

Python обнаруживает, что spam упоминается в bar но ничего не присваивает этой переменной, поэтому он ищет ее в окружающей области видимости foo, Это назначено там, делая del spam утверждение синтаксическая ошибка.

Это ограничение было снято в Python 3.2; теперь вы несете ответственность за то, чтобы не удалять вложенные переменные самостоятельно; вы получите ошибку во время выполнения (NameError) вместо:

>>> def foo():
...     spam = 'eggs'
...     def bar():
...         print(spam)
...     del spam
...     bar()
... 
>>> foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in foo
  File "<stdin>", line 4, in bar
NameError: free variable 'spam' referenced before assignment in enclosing scope

Примером может быть этот:

>>> def outer():
...     x = 0
...     y = (x for i in range(10))
...     del x
... 
SyntaxError: can not delete variable 'x' referenced in nested scope

По сути, это означает, что вы не можете удалить переменные, которые используются во внутренних блоках (в этом случае genexp).

Обратите внимание, что это применимо для python <= 2.7.x и python < 3.2. В python3.2 это не вызывает синтаксическую ошибку:

>>> def outer():
...     x = 0
...     y = (x for i in range(10))
...     del x
... 
>>> 

Смотрите эту ссылку для всей истории изменений.

Я думаю, что семантика python3.2 более правильная, потому что если вы пишете тот же код вне функции, она работает:

#python2.7
>>> x = 0
>>> y = (x for i in range(10))
>>> del x
>>> y.next()     #this is what I'd expect: NameError at Runtime
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <genexpr>
NameError: global name 'x' is not defined

При помещении одного и того же кода в функцию изменяется не только исключение, но и ошибка во время компиляции.

Другие вопросы по тегам