Python、循环和闭包

Python, loops and closures

我是一个相当有经验的 C/C++(在某种程度上,Java)程序员。我正在学习 python,但我对语言的一些奇怪(对于我的背景)行为感到困惑。

我正在学习嵌套函数和闭包(阅读 "Learning Python",这对我来说似乎是一个很好的来源)。

我知道如果我在调用创建的函数时将 def 嵌套在 for 循环中,它会查找捕获的循环变量的最后一个值(因为它通过引用捕获,就像 C++ 程序员所说的那样)

funcs = []
for i in range(4):
    def f():
        print(i)
    funcs.append(f)

和运行程序结果是

>>> for f in funcs:
      f()


3
3
3
3

现在,当我偶然发现这个(在我看来)一个不一致的地方时,我正在思考这个问题:如果我这样做

for i in range(4):
  funcs[i]()


0
1
2
3

更令人费解,如果我这样做

>>> i = 2
>>> funcs[i]()

2

现在,列表 returns 2:

中的所有函数
for f in funcs:
  f()


2
2
2
2

肯定有一些范围相关的问题我没搞清楚

你的函数

def f():
    print(i)

打印i的当前值。

如果你写

for i in range(4):
    funcs[i]()

然后 i 在您执行循环时被设置为 0,1,2,3。这就是 for i in range(4) 的意思。

如果你写

for f in funcs:
    f()

然后 i 继续使用它已有的任何值。

首先,这会创建一个包含四个函数的列表。

funcs = []
for i in range(4):
  def f():
    print(i)
  funcs.append(f)

这些函数中的每一个都查找 i 的值,然后打印它。

循环遍历函数列表并调用每个函数:

>>> for f in funcs:
      f()

如上所述,这些函数查找 i,由于之前完成的 for i in range(4) 循环,现在是 3,所以你得到 3.

的四个打印输出

现在再次循环,使用 i 作为循环变量:

for i in range(4):
  funcs[i]()


0
1
2
3

第一次循环时,i 是 0,所以当函数查找 i 时,它得到 0,然后打印它。然后它变为 1,然后 2,然后 3。

以下代码以另一种方式简单地更改了 i,并调用了一个函数:

>>> i = 2
>>> funcs[i]()

2

您可以调用这些函数中的任何一个,它们仍然会打印 2,因为这是 i 现在的值。你只是迷路了,因为你循环了 range(4) 来创建这些函数,然后你循环了 range(4) 来索引函数列表,你继续重复使用 i,然后你重新分配i 并用它来索引列表。

如果您希望每个函数的打印值 i 固定为您定义函数时的值,最简单的方法是使用默认参数,因为这些参数是在函数执行时计算的被定义而不是被调用时:

funcs = []
for i in range(4):
  def f(num=i):
    print(num)
  funcs.append(f)

这里没有矛盾。 f()i 的值取决于父作用域中 i 的值。在 运行 之后,第一个 for i in range(4) i 具有 range 中最后一项的值,即 3,因此所有后续调用 f() 将打印 3

如果你运行

for i in range(4):
  funcs[i]()

你在每个迭代步骤重新定义 i 的值,所以你得到 0,1,2,3 作为 f 打印的值。正在做

for x in range(4):
   funcs[x]()

不会影响 i 的值,因此在所有函数调用

中,您将获得 3 作为 i 的值

为了完整起见,这是一个替代实现:

def getfunc(i):
    return lambda: i

funcs = []
for i in range(5):
    funcs.append(getfunc(i))

for item in funcs:
    print(item())

我需要一些时间来理解,实际上在这个例子中,

你会发现 f.__closure__None 如果你 print 它,即。与闭包无关,它只是关于过程未定义的局部变量 i 寻找它的值:

它在本地范围内找不到它的值,最后在全局范围内找到它(如 python MRO)