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)
我是一个相当有经验的 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)