我很了解函数式编程。我想创建一个函数列表,每个函数选择一个列表的不同元素。我把我的问题简化为一个简单的例子。当然这是一个Python错误:
fun_list = []
for i in range(5):
def fun(e):
return e[i]
fun_list.append(fun)
mylist = range(10)
print([f(mylist) for f in fun_list])
“显然”它应该返回[0,1,2,3,4]。 然而它返回[4,4,4,4,4]。我怎样才能强迫Python做正确的事情? (之前没有注意到这一点吗?或者我只是很厚?)
这是Python 3.4.0(默认,2014年3月25日,11:07:05)
谢谢, 大卫
答案 0 :(得分:3)
我如何强迫Python做正确的事?
这是一种方法:
fun_list = []
for i in range(5):
def fun(e, _ndx=i):
return e[_ndx]
fun_list.append(fun)
mylist = range(10)
print([f(mylist) for f in fun_list])
这是有效的,因为在执行_ndx
的{{1}}语句时会评估并保存def
的默认值。 (在python中,fun
语句已执行。)
答案 1 :(得分:2)
当然这是一个Python错误......
这是对范围的误解。由于fun()
的所有五个实例都在同一范围内定义,因此它们将具有该范围内所有名称的相同值,包括i
。为了解决这个问题,您需要将包含循环本身的作用域中使用的值分开。这可以通过在完全不同的范围内定义函数来完成。
fun_list = []
def retfun(i):
def fun(e):
return e[i]
return fun
for i in range(5):
fun_list.append(retfun(i))
mylist = range(10)
print([f(mylist) for f in fun_list])
答案 2 :(得分:1)
您可以使用itemgetter
:
from operator import itemgetter
fun_list = []
for i in range(5):
fun_list.append(itemgetter(i))
mylist = range(10)
print([f(mylist) for f in fun_list])
在您的情况下,您正在为呼叫时引用全局i
且值为i
的所有元素分配一个函数,对于所有调用,该函数为4。你需要某种currying。
没有itemgetter
:
def indexer(i): return lambda y: y[i]
fun_list = []
for i in range(5):
fun_list.append(indexer(i))
mylist = range(10)
print([f(mylist) for f in fun_list])