Как выражение печати создает локальные переменные
Вопрос находится в конце этого сообщения.
Первый фрагмент: пустой словарь локальной переменной.
def outer():
x = 1
def inner():
print "Local variables: %s" % locals()
return inner()
print outer()
Вывод: Локальные переменные: {}
Второй фрагмент: печатать внутри функции inner() и создавать локальную переменную.
def outer():
x = 1
def inner():
print x
print "Local variables: %s" % locals()
return inner()
print outer()
Вывод:
1
Local variables: {'x': 1}
Третий фрагмент: del x внутри внутренней функции:
def outer():
x = 1
def inner():
print x
print "Local variables: %s" % locals()
del x
return inner()
print outer()
Вывод:
>>> outer()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in outer
File "<stdin>", line 4, in inner
UnboundLocalError: local variable 'x' referenced before assignment
>>>
Вопросы:
- Во втором фрагменте, как оператор печати создает локальную переменную.
- Если он создает локальную переменную внутри внутренней функции, почему я не могу ее удалить.
Может кто-то, пожалуйста, помогите мне понять это.
Ответы
Ответ 1
В Python, если вы не указали иначе (с инструкцией global
или оператором nonlocal
в версии 3.0+), переменная находится в locals
, если вы ее изменяете (присваиваете ей, del
it, и т.д.) в любом месте функции. *
В первом фрагменте вы никогда не изменяете x
или даже получаете доступ к нему, чтобы он не был локальным. На самом деле его даже не существует. Это легко.
Вторая версия - сложная. x
не является локальным для inner
, потому что вы не изменяете его в inner
. Таким образом, Python ищет его, перемещая область видимости по объему, пока не найдет область, в которой есть эта переменная. И он находит его как локальную переменную в outer
. Это означает, что это переменная закрытия или свободная переменная в inner
. Поскольку функция locals
включает в себя переменные замыкания, а также локальные переменные, вы видите это.
Третья версия, делая del x
, делает x
локальной для inner
. ** Таким образом, она появляется в locals
. Тем не менее, вы пытаетесь print
его, не присваивая ему ничего, так что пока нет никакой ценности. Итак, вы получаете UnboundLocalError
.
Как правило, когда вы понимаете основную идею, которую пытается выполнить Python, обычно очевидно, какая у вас переменная. Но если это когда-либо неясно, подробные правила определены в Именование и привязка.
Если вы хотите понять, как замыкания работают под обложками, вы можете начать с проверки объектов функции. Попробуйте следующее:
def outer():
x = 1
def inner():
print x
print "Local variables: %s" % locals()
return inner
inner = outer()
print inner.func_closure
print inner.func_code.co_freevars
print outer.func_code.co_cellvars
В документах inspect
перечислены все важные члены function
, code
и другие "под обложками" объекты.
Использование модуля dis
для просмотра байт-кода для outer
и inner
также может быть полезно. *** Для Например, если вы запустите этот код, вы увидите LOAD_FAST
для локального, LOAD_DEREF
для ячейки и LOAD_GLOBAL
для глобального.
Но если вы действительно хотите понять, как все это действительно работает, серия статей о таблицах символов в Eli Bendersky "Внутренние элементы Python" блог охватывает почти все очень красиво. (Спасибо Ashwini Chaudhary за то, что он нашел его и указал на него в комментарии.)
* Это проверяется во время компиляции, а не времени выполнения, поэтому попытка запутать его с помощью, например, exec
может успешно сбить с толку как Python, так и себя.
** Обратите внимание, что del
считается как модификацией, так и доступом. Это может быть удивительно, но вы можете видеть, что def foo(): del x
поднимет UnboundLocalError
, потому что del
делает x
локальным, и тот же самый del
не может найти значение.
***... предполагая, что вы используете реализацию Python, которая использует байт-код типа CPython, например, сам CPython (или, конечно, PyPy).
Ответ 2
Python поддерживает вложенные области, рассматривая, как переменные используются во время компиляции. Переменные, которые вы назначаете в функции (или привязываете с помощью import
в функции), считаются локальными, все остальное нелокально. Попытка удалить переменную также отмечает ее как локальную.
Поиск нелокальных имен в родительских областях, и если они не найдены, считаются глобальными.
В вашем втором примере x
ссылается на имя в родительской области. Вы не назначали ему, поэтому это вложенное имя и можно увидеть в локальном пространстве имен. На самом деле это не локальное имя, а свободная переменная; это значение берется из родительской области.
В последнем примере вы пытаетесь удалить x
, сделав его локальным именем. Попытка ссылаться на него, прежде чем что-либо ему было назначено, приводит к исключению.
Все это описано в документации Исполнение документации по ссылке Python. В частности:
Когда имя используется в кодовом блоке, оно разрешается с использованием ближайшего охватывающий объем. Набор всех таких областей видимости для кодового блока называется средой блоков.
Если имя связано в блоке, это локальная переменная этого блока. Если имя связано с уровнем модуля, это глобальная переменная. (The переменные блока кода модуля являются локальными и глобальными.) Если переменная используется в кодовом блоке, но не определена там, она свободна переменная.
Следующие конструкции связывают имена: формальные параметры для функций, import
, определения классов и функций (они связывают класс или имя функции в определяющем блоке) и целевые объекты, которые идентификаторы, если они встречаются в назначении, заголовок цикла for
, в вторая позиция заголовка предложения except
или после as
в с выражение. Оператор import
формы from ... import *
связывает все имена, определенные в импортированном модуле, кроме тех, которые начинаются с подчеркивание. Эта форма может использоваться только на уровне модуля.
Цепочка, встречающаяся в инструкции del
, также считается связанной для эта цель (хотя фактическая семантика состоит в том, чтобы развязать имя). Это является незаконным для развязывания имени, на которое ссылается охватывающая область; компилятор сообщит SyntaxError
.
Ответ 3
есть объяснение Почему я получаю UnboundLocalError, когда переменная имеет значение? на странице faq python docs, похожей на приведенную выше ответы Абарнер и Мартинь.