Как определить свободную переменную в python?

Определения local/global/free variable из python doc:

Если имя связано в блоке, это локальная переменная этого блока, если не объявлено как нелокальная. Если имя связано с уровнем модуля, это глобальная переменная . (Переменные блока кода модуля являются локальными и глобальными.) Если переменная используется в кодовом блоке, но не определена там, это свободная переменная.


Код 1:

>>> x = 0
>>> def foo():
...   print(x)
...   print(locals())
... 
>>> foo()
0
{}

Код 2:

>>> def bar():
...   x = 1
...   def foo():
...     print(x)
...     print(locals())
...   foo()
... 
>>> bar()
1
{'x':1}

Свободные переменные возвращаются locals() при вызове в функциональных блоках, но не в блоках классов.


В Code 1 x - глобальная переменная , и она используется, но не определена в foo().
Однако это не свободная переменная, потому что она не возвращается locals().
Я думаю, это не то, что сказал док. Есть ли техническое определение для свободной переменной?

Ответ 1

Определение свободной переменной: Используется, но ни глобальная, ни связанная.

Например:

  • x не является свободным в коде 1, потому что это переменная глобальная.
  • x не является свободным в bar() в коде 2, потому что это переменная bound.
  • x свободен в foo().

Python делает это различие из-за закрытия. Свободная переменная не определена в текущей среде, т.е. е. набор локальных переменных, а также не глобальная переменная! Поэтому он должен быть определен в другом месте. И это концепция закрытия. В коде 2 foo() закрывается на x, определенном в bar(). Python использует лексическую область. Это означает, что интерпретатор может определить область действия, просто просматривая код.

Например: x называется переменной в foo(), потому что foo() заключен в bar(), а x связан в bar().

Глобальная область обрабатывается специально Python. Было бы возможно, чтобы глобальная область рассматривалась как самая внешняя область, но это не сделано из-за производительности (я думаю). Поэтому невозможно, чтобы x был свободным и глобальным.

Освобождение

Жизнь не так проста. Существуют свободные глобальные переменные. Документы Python (модель исполнения) говорят:

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

>>> x = 42
>>> def foo():
...   global x
...   def baz():
...     print(x)
...     print(locals())
...   baz()
... 
>>> foo()
42
{}

Я сам этого не знал. Мы все здесь, чтобы учиться.

Ответ 2

Из того, что я понял, документация действительно немного неоднозначна по свободным переменным. Существуют свободные глобальные переменные, которые рассматриваются как простые глобалы и лексически связанные свободные переменные. Эли Бэндерски прекрасно подытоживает его в блоге на таблицах символов:

К сожалению, в ядре Python есть стенография, которая может изначально запутать читателей в том, что именно представляет собой "свободную" переменную. К счастью, это очень легкая путаница, которую легко привести в порядок. Ссылка на модель исполнения:

Если переменная используется в кодовом блоке, но не определена там, это свободная переменная.

Это согласуется с формальным определением . В источнике, однако, "бесплатно" фактически используется как сокращенное выражение для "лексически связанной свободной переменной" (т.е. переменные, для которых привязка найдена в охватывающей области), причем "глобальный" используется для обозначения всех оставшихся свободных переменных. Поэтому, читая исходный код CPython, важно помнить, что полный набор свободных переменных включает в себя как переменные, помеченные как "свободные", так и теги, помеченные как "глобальные".

Таким образом, чтобы избежать путаницы, я говорю "лексически связанный", когда хочу ссылаться на переменные, фактически обработанные в CPython, как свободные.

(акцент мой)

Причина, по которой эта стенография была использована, вероятно, связана с тем, что, когда у вас есть глобальная свободная переменная, в выпуске байт-кода не происходит никаких изменений. Если переменная global является "свободной" или если она не изменяет тот факт, что поиск этого имени будет использовать LOAD_GLOBAL в обоих случаях. Таким образом, глобальные свободные переменные не все таковы.

С другой стороны, лексически связанные переменные обрабатываются специально и заключены в объекты cell, объекты - это пространство для хранения лексически связанных переменных и находятся в атрибуте __closure__ для данной функции. Для них, которая исследует ячейки, присутствующие для свободных переменных, создается специальная команда LOAD_DEREF. Описание инструкции LOAD_DEREF:

LOAD_DEREF(i)

Загружает ячейку, содержащуюся в слоте я ячейки и свободное хранилище переменных

Таким образом, свободные переменные Python меняют только как концепцию в ситуациях, когда определение для объекта, имеющего состояние, лексически (то есть статически) вложено в другое определение для объекта, который имеет состояние.

Ответ 3

Переменные - это не что иное, как зарезервированные ячейки памяти для хранения значений. Это означает, что при создании переменной вы сохраняете некоторое пространство в памяти.

В зависимости от типа данных переменной интерпретатор выделяет память и решает, что можно сохранить в зарезервированной памяти. Поэтому, присваивая переменным разные типы данных, вы можете хранить целые числа, десятичные знаки или символы в этих переменных.

Назначение значений для переменных

Переменные Python не требуют явного объявления для резервирования пространства памяти. Объявление присваивается автоматически, когда вы присваиваете значение переменной. Знак равенства (=) используется для присвоения значений переменным.

Ответ 4

Не существует явного ключевого слова для объявления свободной переменной в Python. Основываясь на определении функции и внутренних и внешних ее утверждений, Python будет классифицировать переменные в связанные, ячейки и свободные переменные.

Следующий пример иллюстрирует это понятие, используя объект кода функции, который инкапсулирует переменные, упомянутые в предыдущем абзаце.

def func(arg1, arg2=2):
    def inner_func(arg3=arg2):
        return arg1, arg3
    arg1, arg2 = None
    return inner_func

Для 'func':

• arg1 и arg2 - связанные переменные

• arg1 - это переменная ячейки, поскольку она является свободной переменной внутри 'inner_func'

• Нет свободных переменных.

func.__code__.co_varnames

('arg1', 'arg2', 'inner_func')

func.__code__.co_cellvars

( 'арг1',)

func.__code__.co_freevars

()

Для 'inner_func':

• arg3 является связанной переменной

• arg1 - свободная переменная

• Нет переменных ячеек

inner_func.__code__.co_varnames

( 'arg3',)

inner_func.__code__.co_freevars

( 'арг1')

inner_func.__code__.co_cellvars

()