一道好题
题目如下:
def f():
l = []
for i in range(4):
def a(x):
return x * i
l.append(a)
return l
print([e(2) for e in f()]) # [6, 6, 6, 6]
代码可以翻译成这样:
def f():
l = []
for i in range(4):
def a(x):
return x * i
l.append(a)
return l
l_curr = []
for e in f():
l_curr.append(e(2))
print(l_curr)
代码的执行过程如下:
-
代码从上到下执行,进入函数
f()
-
初始化空列表
l
-
进入
for
循环 -
i=0
,执行l.append(a)
python调用函数时加括号表示调用函数的最终执行结果,不加括号表示调用函数对象,所以这里列表追加的时函数对象。此时
l=[<function f.<locals>.a at 内存地址1>]
-
i=1
,执行l.append(a)
此时
l=[<function f.<locals>.a at 内存地址1>,<function f.<locals>.a at 内存地址2>]
-
i=2
,执行l.append(a)
此时
l=[<function f.<locals>.a at 内存地址1>,<function f.<locals>.a at 内存地址2>],<function f.<locals>.a at 内存地址3>
-
i=3
,执行l.append(a)
此时
l=[<function f.<locals>.a at 内存地址1>,<function f.<locals>.a at 内存地址2>],<function f.<locals>.a at 内存地址3>,<function f.<locals>.a at 内存地址4>
-
函数
f()
执行完毕,return l
此时
l=[<function f.<locals>.a at 内存地址1>,<function f.<locals>.a at 内存地址2>],<function f.<locals>.a at 内存地址3>,<function f.<locals>.a at 内存地址4>
-
初始化空列表
l_curr
-
进入
for
循环,开始遍历f()
此时的
f()
是带括号的,所以引用的是最终返回的列表 -
遍历第一个元素
<function f.<locals>.a at 内存地址1>
,执行2 * 3 = 6
,这个函数存储的i
是i的引用,此时的i=3
-
遍历第二个元素
<function f.<locals>.a at 内存地址2>
,执行2 * 3 = 6
,这个函数存储的i
是i的引用,此时的i=3
-
遍历第三个元素
<function f.<locals>.a at 内存地址3>
,执行2 * 3 = 6
,这个函数存储的i
是i的引用,此时的i=3
-
遍历第四个元素
<function f.<locals>.a at 内存地址4>
,执行2 * 3 = 6
,这个函数存储的i
是i的引用,此时的i=3
-
遍历完毕,返回列表
l_curr=[6, 6, 6, 6]
类比
def f():
l = []
for i in range(4):
def a(x, i=i):
return x * i
l.append(a)
return l
l_curr = []
for e in f():
l_curr.append(e(2))
print(l_curr)
区别是:def a(x, i=i)
在这个程序中,函数f()
中的列表l
每次追加的函数a
中存储的i
是当前的i值,而不是引用。所以四次的i
值分别为0, 1, 2, 3
。
所以最终的结果为l_curr=[0, 2, 4, 6]