如何在python中定义自由变量?

kev*_*kev 32 python

python doc中本地/全局/自由变量定义:

如果名称绑定在块中,则它是该块的局部变量,除非声明为非本地.如果名称在模块级别绑定,则它是全局变量.(模块代码块的变量是局部的和全局的.)如果在代码块中使用了变量但在那里没有定义,则它是一个自由变量.


代码1:

>>> x = 0
>>> def foo():
...   print(x)
...   print(locals())
... 
>>> foo()
0
{}
Run Code Online (Sandbox Code Playgroud)

代码2:

>>> def bar():
...   x = 1
...   def foo():
...     print(x)
...     print(locals())
...   foo()
... 
>>> bar()
1
{'x':1}
Run Code Online (Sandbox Code Playgroud)

自由变量locals()在函数块中调用时返回,但不在类块中调用.


In Code 1,x是一个全局变量,它已被使用但未定义foo().
然而,它不是一个自由变量,因为它不是由它返回的locals().
我认为这不是医生所说的.是否有自由变量的技术定义?

nal*_*ply 39

自由变量的定义:已使用,但既不是全局变量也不是绑定变量.

例如:

  1. x在代码1中不是免费的,因为它是一个全局变量.
  2. xbar()在代码2中不是免费的,因为它是一个绑定变量.
  3. x是免费的foo().

由于闭包,Python做出了这种区分.自由变量未在当前环境中定义,即局部变量的集合,也不是全局变量!因此必须在别处定义.这就是闭包的概念.在代码2中,foo()关闭x定义于bar().Python使用词法范围.这意味着,解释器只需查看代码即可确定范围.

例如:x被称为变量in foo(),因为foo()被包围bar(),并被x绑定bar().

全局范围由Python专门处理.可以将全局范围视为最外层的范围,但由于性能(我认为),这不是完成的.因此,不可能x自由的全球的.

豁免

生活并非如此简单.存在自由的全局变量.Python文档(执行模型)说:

全局语句与同一块中的名称绑定操作具有相同的范围.如果自由变量的最近封闭范围包含全局语句,则将自由变量视为全局变量.

>>> x = 42
>>> def foo():
...   global x
...   def baz():
...     print(x)
...     print(locals())
...   baz()
... 
>>> foo()
42
{}
Run Code Online (Sandbox Code Playgroud)

我自己也不知道.我们都在这里学习.

  • @nalply“免费全球”在这里有点夸大其词。有或没有全局 x 行,print(x) 中的 x 取自全局范围。在 baz 中添加非局部 x 会导致语法错误,因为 x 不是自由变量,而是全局变量。@kev 为什么?因为 Python 开发者将它们定义为独占的,句号。他们专门为**闭包**和**封闭范围**定义了“自由变量”。实际上:你可以在`baz`中添加`global y`,但不能添加`nonlocal y`;一些 `a` 不能同时是 `global` 和 `nonlocal`。全局和局部范围也是如此:你不能在 `baz` 中的 `print(x)` 之后使用 `x = 100`(范围混合)。 (3认同)
  • 此外,“自由变量被视为全局变量”可能有点误导,因为它表明某些自由变量被视为全局变量,因此既是 **global** 又是 **free**,这是错误的。使用 `baz.__code__.co_freevars` 检查 `foo` 中的 `baz` 显示没有自由变量。他们可能意味着“自由变量的候选者被视为全局变量”。 (3认同)

Jim*_*ard 6

据我了解,文档对自由变量确实有点含糊不清。有免费的全局变量被视为纯全局和词法约束自由变量。Eli Bendersky 在关于符号表博客文章中很好地总结了它:

不幸的是,Python 的核心中有一个速记,最初可能会让读者混淆“自由”变量的确切构成。幸运的是,这是一个非常轻微的混乱,很容易整理。执行模型参考说:

如果一个变量在代码块中使用但未在那里定义,则它是一个自由变量。

这与正式的定义是一致的。然而,在源代码中,“free”实际上用作“词法绑定的自由变量”的简写(即在封闭范围内找到,“global”用于指代所有剩余的自由变量变量。因此,在阅读 CPython 源代码时,重要的是要记住,完整的自由变量集包括专门标记为“自由”的变量以及标记为“全局”的变量。

因此,为了避免混淆,当我想将 CPython 中实际处理的变量称为自由变量时,我会说“词法绑定”。

(强调我的)

使用这种速记的原因可能是因为当您拥有全局自由变量时,发出的字节码实际上没有任何变化。如果global变量是“自由”的,或者不是“自由”,则不会改变LOAD_GLOBAL在这两种情况下都将使用该名称的查找这一事实。所以全局自由变量并不是那么特别。

另一方面,词法绑定的变量被特殊对待并被封闭在cell对象中,对象是词法绑定的自由变量的存储空间,位于__closure__给定函数的属性中。LOAD_DEREF为这些创建了一个特殊指令,用于检查存在自由变量的单元格。该LOAD_DEREF指令的描述是:

LOAD_DEREF(i)

加载包含在单元格槽 i 中的单元格和自由变量存储

因此,在 Python 中,自由变量仅在具有状态的对象的定义在词法上(即静态地)嵌套在具有状态的对象的另一个定义中的情况下作为概念有所不同。