一道好题
一道好题
题目如下:
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=[
.a at 内存地址1>] -
i=1
,执行l.append(a)
此时
l=[
.a at 内存地址1>, .a at 内存地址2>] -
i=2
,执行l.append(a)
此时
l=[
.a at 内存地址1>, .a at 内存地址2>], .a at 内存地址3> -
i=3
,执行l.append(a)
此时
l=[
.a at 内存地址1>, .a at 内存地址2>], .a at 内存地址3>, .a at 内存地址4> -
函数
f()
执行完毕,return l
此时
l=[
.a at 内存地址1>, .a at 内存地址2>], .a at 内存地址3>, .a at 内存地址4> -
初始化空列表
l_curr
-
进入
for
循环,开始遍历f()
此时的
f()
是带括号的,所以引用的是最终返回的列表 -
遍历第一个元素
,执行.a at 内存地址1> 2 * 3 = 6
,这个函数存储的i
是i的引用,此时的i=3
-
遍历第二个元素
,执行.a at 内存地址2> 2 * 3 = 6
,这个函数存储的i
是i的引用,此时的i=3
-
遍历第三个元素
,执行.a at 内存地址3> 2 * 3 = 6
,这个函数存储的i
是i的引用,此时的i=3
-
遍历第四个元素
,执行.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]