Как определить свободную переменную в 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
()