我試圖從函數返回一個函數列表,每個函數都使用來自外部作用域的變量。這不起作用。下面是一個演示發生了什麼的示例:有Python 2.7函數記住值而不是引用? Closure Weirdness
a = []
for i in range(10):
a.append(lambda x: x+i)
a[1](1) # returns 10, where it seems it should return 2
爲什麼會發生這種情況,以及如何在python 2.7中解決它?
我試圖從函數返回一個函數列表,每個函數都使用來自外部作用域的變量。這不起作用。下面是一個演示發生了什麼的示例:有Python 2.7函數記住值而不是引用? Closure Weirdness
a = []
for i in range(10):
a.append(lambda x: x+i)
a[1](1) # returns 10, where it seems it should return 2
爲什麼會發生這種情況,以及如何在python 2.7中解決它?
i
每次都指向相同的變量,所以i
在所有lambda表達式中都是9,因爲這是循環結束時的值i
。簡單的解決方法涉及到一個默認參數:
lambda x, i=i: x+i
這在拉姆達的定義時結合循環的i
一個局部變量i
的價值。
另一個解決方法是定義一個lambda定義另一個lambda,並調用第一拉姆達:
(lambda i: lambda x: x+i)(i)
此行爲使更多一點意義,如果你考慮這個問題:
def outerfunc():
def innerfunc():
return x+i
a = []
for i in range(10):
a.append(innerfunc)
return a
這裏, innerfunc
定義了一次,因此直觀地意識到你只使用一個函數對象,並且你不希望循環創建十個不同的閉包。使用lambda它不會外觀就像函數只定義一次,它看起來像你每次在循環中定義它新鮮,但實際上它在功能上與長版本相同。
因爲當你定義匿名函數(lambda表達式)但是它被調用時我沒有得到評估。你可以在a[1](1)
之前加del i
來看到這個:你會在a[1](1)
行上得到NameError: global name 'i' is not defined
。
你需要每一次i的值修正爲lambda表達式,像這樣:
a = [lambda x, i=i: x+i for i in range(10)]
a[1](1) # returns 2
另外一個更通用的解決方案 - 還沒有lambda表達式:
import operator
from functools import partial
a = []
for i in range(10):
a.append(partial(operator.add, i))
a[1][(1) # returns 2
關鍵方面在這裏是functools.partial。
呵呵。真的沒有辦法讓每個lambda持有自己的價值嗎?我認爲這是關閉的關鍵。 – Chironex
Kudos!有意義,但很明顯... – mjv
@Chironex,閉包是變量名稱不值 –