Как определить свободную переменную в Python?
Определения локальных / глобальных / свободных переменных из документа python:
Если имя связано в блоке, оно является локальной переменной этого блока, если не объявлено как нелокальное. Если имя связано на уровне модуля, это глобальная переменная. (Переменные блока кода модуля являются локальными и глобальными.) Если переменная используется в блоке кода, но не определена там, это свободная переменная.
Код 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()
,
Я думаю, что это не то, что сказал док. Есть ли техническое определение для свободной переменной?
5 ответов
Определение свободной переменной: используется, но не глобально и не связано.
Например:
x
не является свободным в коде 1, потому что это глобальная переменная.x
не свободен вbar()
в коде 2, потому что это связанная переменная.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
{}
Я сам этого не знал. Мы все здесь, чтобы учиться.
Из того, что я понял, документация действительно немного неоднозначна в отношении свободных переменных. Существуют свободные глобальные переменные, которые рассматриваются как простые глобальные переменные и лексически связанные свободные переменные. Эли Бендерский суммирует это в своем блоге по символическим таблицам:
К сожалению, в ядре Python есть условное обозначение, которое может сначала запутать читателей относительно того, что именно представляет собой "свободную" переменную. К счастью, это очень легкая путаница, которую легко привести в порядок. Ссылка на модель исполнения гласит:
Если переменная используется в блоке кода, но не определена там, это свободная переменная.
Это согласуется с формальным определением. В источнике, однако, "свободный" фактически используется как сокращение для "лексически связанной свободной переменной" (т. Е. Переменных, для которых была найдена привязка во внешней области видимости), причем "глобальный" используется для ссылки на все оставшиеся свободные переменные. Поэтому при чтении исходного кода CPython важно помнить, что полный набор свободных переменных включает в себя как переменные, помеченные как "свободные", так и переменные, помеченные как "глобальные".
Таким образом, чтобы избежать путаницы, я говорю "лексически связанный", когда хочу сослаться на переменные, которые фактически рассматриваются в CPython как свободные.
(акцент мой)
Причина использования этого сокращения, вероятно, заключается в том, что когда у вас есть глобальная свободная переменная, в байт-коде вообще не происходит никаких изменений. Если global
переменная "свободна" или если она не изменяет тот факт, что поиск по этому имени будет использовать LOAD_GLOBAL
в обоих случаях. Так что глобальные свободные переменные не такие уж особенные.
С другой стороны, лексически связанные переменные обрабатываются специально и заключаются в cell
объекты, объекты являются пространством хранения для лексически связанных свободных переменных и расположены в __closure__
атрибут для данной функции. Специальный LOAD_DEREF
для них создана инструкция, которая проверяет наличие ячеек на наличие свободных переменных. Описание для LOAD_DEREF
инструкция это:
LOAD_DEREF(i)
Загружает ячейку, содержащуюся в слоте i ячейки, и свободное хранилище переменных
Таким образом, в Python свободные переменные имеют значение как концепция только в ситуациях, когда определение для объекта, который имеет состояние, лексически (то есть статически) вложено в другое определение для объекта, который имеет состояние.
Просто для того, чтобы новички не вводили в заблуждение, приведенный выше комментарий Никхила, начинающийся с "Переменные - это всего лишь зарезервированные области памяти для хранения значений" совершенно неправильно для Python.
В Python есть "имена" и "значения". Значения имеют типы, а не имена. Память зарезервирована для значений, а не для имен. Например, у нас может быть x = 1, за которым в коде позже следует x = "строка", за которым следует x = [3, 9.1]. В ходе этих назначений имя x сначала указывает на целое число, затем на строку и, наконец, на список. Когда назначение выполнено, имя в левой части назначения делается так, чтобы указывать на значение в правой части назначения. Значения могут быть неизменяемыми (неизменяемыми) или изменяемыми (изменяемыми). Целые числа, строки, кортежи и т. Д. Являются неизменяемыми; списки и т. д. являются изменяемыми. Так как целые числа неизменны, когда есть два утверждения, подобные этому:
х = 4
х = х +1
второе утверждение заставляет х указывать на новое значение 5, оно не меняет значение в ячейке памяти, на которое указывает х, с 4 на 5!
Это очень отличная модель от языка С!
Нет явного ключевого слова для объявления свободной переменной в 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
('arg1')
inner_func.__code__.co_cellvars
()
Переменные - это не что иное, как зарезервированные области памяти для хранения значений. Это означает, что когда вы создаете переменную, вы резервируете некоторое пространство в памяти.
На основе типа данных переменной интерпретатор выделяет память и решает, что можно сохранить в зарезервированной памяти. Поэтому, назначая переменным разные типы данных, вы можете хранить целые числа, десятичные числа или символы в этих переменных.
Присвоение значений переменным
Переменные Python не требуют явного объявления для резервирования места в памяти. Объявление происходит автоматически, когда вы присваиваете значение переменной. Знак равенства (=) используется для присвоения значений переменным.