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

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

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

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

Ответ 1

Следующее приводит к выполнению:

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

Ответ 2

Пример может быть следующим:

>>> 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

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