Подтвердить что ты не робот

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

Вопрос находится в конце этого сообщения.

Первый фрагмент: пустой словарь локальной переменной.

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

Вопросы:

  • Во втором фрагменте, как оператор печати создает локальную переменную.
  • Если он создает локальную переменную внутри внутренней функции, почему я не могу ее удалить.

Может кто-то, пожалуйста, помогите мне понять это.

4b9b3361

Ответ 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.