Исключение Python 3 удаляет переменную в закрывающей области по неизвестной причине
У меня есть следующий код:
def foo():
e = None
try:
raise Exception('I wish you would except me for who I am.')
except Exception as e:
print(e)
print(e)
foo()
В Python 2.7 это выполняется как ожидалось и печатает:
I wish you would except me for who I am.
I wish you would except me for who I am.
Однако в Python 3.x печатается первая строка, а вторая - нет. Кажется, что она удаляет переменную в охватывающей области, предоставляя мне следующую трассировку из последнего оператора печати:
Traceback (most recent call last):
File "python", line 9, in <module>
File "python", line 7, in foo
UnboundLocalError: local variable 'e' referenced before assignment
Это почти как если инструкция del e
вставляется после блока except
. Есть ли какие-либо аргументы в пользу такого поведения? Я мог бы это понять, если разработчики Python хотели, чтобы блоки имели свою собственную локальную область, а не течь в окружающую область, но почему она должна удалять переменную во внешней области, которая ранее была назначена?
Ответы
Ответ 1
Цитирование документации try
,
Когда исключение назначено с помощью as target
, оно очищается в конце предложения except. Это как если бы
except E as N:
foo
был переведен на
except E as N:
try:
foo
finally:
del N
Это означает, что исключение должно быть назначено другому имени, чтобы иметь возможность ссылаться на него после предложения except. Исключения очищаются, потому что с привязкой к ним трассировки они образуют опорный цикл с фреймом стека, сохраняя все локали в этом фрейме до тех пор, пока не произойдет следующая сборка мусора.
Это описано в этих двух PEP.
Ответ 2
Я написал пост в блоге об этом еще в 2013 году. Это функция, которая предотвращает циклические ссылки.
https://www.wefearchange.org/2013/04/python-3-language-gotcha-and-short.html