2014-04-16 27 views
3

我很好的理解函數式編程。我想創建一個函數列表,每個函數都選擇一個不同的列表元素。我已經將我的問題簡化爲一個簡單的例子。肯定這是一個Python bug:在python中創建函數列表(python函數關閉bug?)

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秒)

謝謝, 大衛

+2

當內部功能'fun'運行i的值是4。線'返回E [I]'運行時調用該函數,而不是當你定義該函數。 – Alex

回答

2

我怎麼能強迫的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默認值進行評估並且執行fundef語句時保存。 (在Python中,def陳述執行

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]) 
2

這無疑是一個Python的bug ......

這是範圍的誤解。由於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]) 
+1

這不是對範圍的誤解。我期望在當前的環境中對函數進行評估;而不是未來的某個人。合理的做法是將函數與當前環境的*副本*相關聯,而不是在所有值發生變化後都與環境相關聯。這就是爲什麼我們在特定的上下文中定義函數的原因...... – user3539674

+0

如果它給出了當前範圍的*副本,那麼它將無法與非本地範圍一起工作,因爲這些範圍中的值永遠不會改變。 –