50

来自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(),而不是在类块中。


in Code 1,x是一个全局变量,它被使用但未在foo().
但是它不是一个自由变量,因为它不是由locals().
我认为这不是医生所说的。自由变量有技术定义吗?

4

5 回答 5

50

自由变量的定义:已使用,但既不是 global 也不是 bound

例如:

  1. x在代码 1 中不是免费的,因为它是一个全局变量。
  2. x在代码 2中不是免费的bar(),因为它是一个绑定变量。
  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
{}

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

于 2012-10-16T16:33:11.033 回答
7

据我了解,文档在自由变量上确实有点模棱两可。有被视为普通全局变量的自由全局变量和词法绑定的自由变量Eli Bendersky 在关于符号表的博客文章中很好地总结了它:

不幸的是,Python 的核心中有一个简写,最初可能会使读者混淆究竟什么是“自由”变量。幸运的是,这是一个非常轻微的混乱,很容易整理。执行模型参考说:

如果变量在代码块中使用但未在此处定义,则它是自由变量。

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

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

(强调我的)

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

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

LOAD_DEREF(i)

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

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

于 2015-12-21T16:10:10.443 回答
3

为了让新手不会被误导,Nikhil 上面的评论以“变量只不过是用于存储值的保留内存位置”开头。对 Python 来说是完全错误的。

在 Python 中有“名称”和“值”。值有类型,而不是名称。内存空间是为值保留的,而不是为名称保留的。例如,我们可以在代码中让 x = 1 后面跟着 x = "a string",后面跟着 x = [3, 9.1]。在这些赋值过程中,名称 x 首先指向一个整数,然后指向一个字符串,最后指向一个列表。赋值完成后,赋值左侧的名称指向赋值右侧的值。值可以是不可变的(不可变的)或可变的(可变的)。整数、字符串、元组等是不可变的;列表等是可变的。由于整数是不可变的,当有两个这样的语句时:

x = 4

x = x +1

第二条语句使 x 指向一个新值 5,它不会将 x 指向的内存位置中的值从 4 更改为 5!

这是一个与 C 语言非常不同的模型!

于 2018-11-30T08:07:58.460 回答
2

Python 中没有明确的关键字来声明自由变量。根据函数的定义以及函数内部和周围的语句,Python 会将变量分为绑定变量、单元变量和自由变量。

下面的示例使用函数的代码对象说明了这个概念,该对象封装了上一段中提到的变量。

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

对于“函数”:

arg1arg2绑定变量

arg1是一个单元变量,因为它是“ inner_func ”内部的一个自由变量

• 没有自由变量。

func.__code__.co_varnames

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

func.__code__.co_cellvars

('arg1',)

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

()

于 2017-06-04T12:31:21.383 回答
0

变量只不过是用于存储值的保留内存位置。这意味着当您创建一个变量时,您会在内存中保留一些空间。

根据变量的数据类型,解释器分配内存并决定可以在保留内存中存储什么。因此,通过为变量分配不同的数据类型,您可以在这些变量中存储整数、小数或字符。

为变量赋值

Python 变量不需要显式声明来保留内存空间。当您为变量赋值时,声明会自动发生。等号 (=) 用于为变量赋值。

于 2017-02-04T15:52:19.607 回答