python 中的工厂函数,简单理解就是:高阶函数的一种,可以返回函数的函数。
一般我们可以写出这样的工厂函数:
def func(x):
return lambda: x
然后验证一下:
print(func(2)())
# 2
输出为2。没错,就是我们期待的结果。
然后,再来一个带循环体的(错误的示范!):
def multi(x):
ret = []
for ele in range(x):
ret.append(lambda: ele)
return ret
- 输出验证一下:
for action in multi(5):
print(action(),end="\t")
# 4 4 4 4 4
这时候就出问题了。lambda 并没有记住我们所期待的
ele
而是记住了最后一个ele
.
- 如何解决?
- 全部设置默认参数就好了。
那么修改后的代码如下(正确的示范):
def multi(x):
ret = []
for ele in range(x):
ret.append(lambda k=ele: k)
return ret
- 输出如下
for action in multi(5):
print(action(), end="\t")
# 0 1 2 3 4
对于之前的func()
函数呢?当然也可以设置默认参数:
def func(x):
return lambda p=x: p
通过对默认参数的设置,代码就不会依赖运行时的检查,而是在定义时就明确知道状态信息。
- 分析:为何定义了默认参数值,就可以避免循环中出现的问题?
以multi(x)
函数为例:其等价形式为:
def multi(x):
ret = []
for ele in range(x):
def temp(t=ele):
return t
ret.append(temp)
return ret
- 在
def multi(x):
中,变量ele
和函数temp()
是同时被创建的。都是在multi(x)
函数被调用时创建。 - 而
def temp():
中的返回值t
是在temp()
函数被调用的时候创建。 - 而且这两个函数总是不能同时被调用。总是需要
multi(x)()
这样的方式去调用。也就导致了t
总是在ele
创建之后被创建。 - 所以在这样的场景下,如果不设置默认值,
t
返回的总是最后一个ele
的值。 - 而,如果给
temp()
函数设置了默认参数temp(t=ele)
,就强制在temp()
函数被创建的时候,就创建变量t
.这样刚好就达到了预期:在遍历的过程中,不断地去改变ele
的值,同时t
也随着ele
的改变而改变。(如果不做这种默认参数的设置。对t
的赋值实际上只发生了一次,也就是在temp()
被调用的时候发生了一次。)
总结:
x,ret,ele
都是在multi(x)
被调用的时候创建的temp()
的返回值,是在temp()
被调用的时候创建的temp()
总是在multi(x)
被调用之后才调用- 要想让
temp()
得到遍历中的每次ele
的值,就可以通过默认参数的方式,让temp()
在遍历的过程中就得到ele
的值。(这也许是目前唯一的办法)
其实表述的不是很准确:python中所有对变量的赋值操作实质上都是对对象的引用的修改。
在上述行为中,也就是不断改变t
的引用。t = ele = num(obj)
.ele
每次引用不同的对象,而t
总是做与ele
相同的引用。