Python:绑定如何工作

11 投票
5 回答
9145 浏览
提问于 2025-04-16 07:41

我正在尝试理解,Python 中的变量绑定到底是怎么回事。我们来看这个例子:

def foo(x):
    def bar():
        print y
    return bar

y = 5
bar = foo(2)
bar()

这个例子打印出 5,这对我来说是合理的。

def foo(x):
    def bar():
        print x
    return bar
x = 5
bar = foo(2)
bar()

这个例子打印出 2,这就有点奇怪了。在第一个例子中,Python 在执行时查找变量,而在第二个例子中,它是在创建方法时查找的。为什么会这样呢?

为了更清楚:这真的很酷,正是我想要的效果。不过,我对内部的 bar 函数是如何获取它的上下文感到困惑。我想了解一下,背后到底发生了什么。

补充说明

我知道局部变量的优先级更高。我很好奇,Python 是怎么知道在执行时要从我之前调用的函数中获取参数的。bar 是在 foo 中创建的,而 x 已经不存在了。它是在函数创建时把这个 x 绑定到参数值上的吗?

5 个回答

1

在这两个例子中,查找变量是在程序运行时进行的。唯一的区别是第一个例子里有一个本地定义的变量 x,而第二个例子里没有本地定义的变量 y

当执行 ...

def foo(x):
    def bar():
        print y

    return bar

y = 5
bar = foo(2)
bar()

... 时,print 语句查找名为 y 的变量,只在全局范围内找到了它,所以使用了这个全局变量,打印出 "5"。

在 ...

def foo(x):
    def bar():
        print x

    return bar

x = 5
bar = foo(2)
bar()

... 当查找发生时,确实有一个作用域内的变量 x 被定义了——这个变量在调用 foo 函数时被固定为 "5"。

关键在于,参数是在传递给函数时被评估的,所以外部函数 foo 在被调用时会评估传入的参数。这实际上在 foo 函数的上下文中创建了一个名为 x 的变量,因此每当 bar 执行时,它看到的是这个变量,而不是全局定义的那个。

这有时会让人感到困惑,比如在以下代码中:

lst = []
for i in range(5):
    x = i
    lst.append(lambda: x)

for func in lst:
    print func()  # prints 4 4 4 4 4

你需要做:

lst = []
for i in range(5):
    def _func(x):
        return lambda: x

    lst.append(_func(i))

for func in lst:
    print func()  # prints 0 1 2 3 4
7

你提到的问题是关于Python中变量的作用域,主要有两种类型:词法作用域和动态作用域。为了让你更清楚,Python定义了以下四种作用域。

  1. 最内层的作用域,首先会被查找,里面包含了局部变量。
  2. 任何外层函数的作用域,从最近的外层开始查找,里面包含了非局部但也不是全局的变量。
  3. 倒数第二层的作用域,包含当前模块的全局变量。
  4. 最外层的作用域(最后查找的),是包含内置变量的命名空间。

在第一个例子中,"y"是在函数bar外面定义的,Python会先查找最内层的作用域,然后逐层向上查找,直到在模块中找到全局变量"y"。

在第二个例子中,"x"是由函数foo(x)定义的,x属于外层函数的作用域,当它在bar函数内部被打印时。简单来说,这里定义了一个闭包。

如果你想进一步了解Python中的作用域,我发现这篇文章非常值得一读。

7

第二个例子实现了一个叫做闭包的概念。这里的函数bar引用了它周围环境中的变量x,也就是函数foo里的那个变量。这是在引用全局变量x之前发生的。

你还可以看看这个问题 你能解释一下闭包在Python中的应用吗?

撰写回答