在python中创建函数列表(python函数闭包bug?)

时间:2014-04-16 05:04:52

标签: python function closures

我很了解函数式编程。我想创建一个函数列表,每个函数选择一个列表的不同元素。我把我的问题简化为一个简单的例子。当然这是一个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)

谢谢, 大卫

3 个答案:

答案 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])