簡體   English   中英

如何創建 lambda 列表(在列表理解/for 循環中)?

[英]How do I create a list of lambdas (in a list comprehension/for loop)?

我想從 Python 中的常量列表創建一個 lambda 對象列表; 例如:

listOfNumbers = [1,2,3,4,5]
square = lambda x: x * x
listOfLambdas = [lambda: square(i) for i in listOfNumbers]

這將創建一個 lambda 對象列表,但是,當我運行它們時:

for f in listOfLambdas:
    print f(),

我希望它會打印

1 4 9 16 25

相反,它打印:

25 25 25 25 25

似乎 lambda 表達式都被賦予了錯誤的參數。 我做錯了什么,有沒有辦法解決? 我想我在 Python 2.4 中。

編輯:多一點嘗試的東西,這樣想出了這個:

listOfLambdas = []
for num in listOfNumbers:
    action = lambda: square(num)
    listOfLambdas.append(action)
    print action()

打印從 1 到 25 的預期方塊,然后使用較早的打印語句:

for f in listOfLambdas:
    print f(),

仍然給了我所有的25秒。 現有的 lambda 對象在這兩個打印調用之間有何變化?

相關問題: 為什么 map() 和 list comprehension 的結果不同?

你有:

listOfLambdas = [lambda: i*i for i in range(6)]

for f in listOfLambdas:
    print f()

輸出:

25
25
25
25
25
25

你需要咖喱! 除了美味之外,還可以使用這個默認值“hack”。

listOfLambdas = [lambda i=i: i*i for i in range(6)]

for f in listOfLambdas:
    print f()

輸出:

0
1
4
9
16
25

注意i=i 這就是魔法發生的地方。

我猜測您在列表推導式中創建的 lambda 綁定到變量 i 最終以 5 結束。因此,當您事后評估 lambda 時,它們都綁定到 5 並最終計算25. 在你的第二個例子中,同樣的事情發生在 num 上。 當你在循環內評估 lambda 時,它的 num 沒有改變,所以你得到了正確的值。 循環后,num 為 5...

我不太確定你要做什么,所以我不確定如何提出解決方案。 這個怎么樣?

def square(x): return lambda : x*x
listOfLambdas = [square(i) for i in [1,2,3,4,5]]
for f in listOfLambdas: print f()

這給了我預期的輸出:

1
4
9
16
25

另一種思考方式是 lambda 在創建時“捕獲”其詞法環境。 所以,如果你給它num ,它實際上不會解析該值,直到它被調用。 這既令人困惑又強大。

當函數語句被執行時,它們被綁定到它們的(詞法上)封閉作用域。

在你的片段中,lambda表達式必然要在全球范圍內,由於for套房都沒有在Python獨立作用域單元執行。 for循環結束時, num綁定在封閉范圍內。 演示:

for num in range(1, 6):
    pass
assert num == 5 # num is now bound in the enclosing scope

因此,當您在for循環中綁定標識符時,您實際上是在操縱封閉范圍。

for num in range(1, 6):
    spam = 12
assert num == 5 # num is now bound in the enclosing scope
assert spam == 12 # spam is also bound in the enclosing scope

列表理解的相同處理:

[num for num in range(1, 6)]
assert num == 5

腦洞大開,我知道。 任何人,憑借我們新發現的知識,我們可以確定您正在創建的 lambda 表達式是指在封閉范圍內綁定的(單個) num標識符。 這應該使這更有意義:

functions = []
for number in range(1, 6):
    def fun():
        return number
    functions.append(fun)
assert all(fun() == 5 for fun in functions)
assert all(fun() is number for fun in functions)

這是更酷的部分:

# Same as above -- commented out for emphasis.
#functions = []
#for number in range(1, 6):
#    def fun():
#        return number
#    functions.append(fun)
#assert all(fun() == 5 for fun in functions)
#assert all(fun() is number for fun in functions)
number = 6 # Rebind 6 in the scope and see how it affects the results.
assert all(fun() == 6 for fun in functions) 

因此,解決方案當然是為您要綁定的每個number創建一個新的封閉范圍。 在 Python 中,您可以使用模塊、類和函數創建新的封閉作用域。 使用一個函數只是為另一個函數創建新的封閉作用域是很常見的。

在 Python 中,閉包是一個返回另一個函數的函數 有點像函數構造函數。 在以下示例中查看get_fun

def get_fun(value):
    """:return: A function that returns :param:`value`."""
    def fun(): # Bound to get_fun's scope
        return value
    return fun

functions = []
for number in range(1, 6):
    functions.append(get_fun(number))
assert [fun() for fun in functions] == range(1, 6)

因為get_fun是一個函數,所以它有自己的內部作用域。 每次使用值調用get_fun時,都會創建一個小表來跟蹤其中的綁定; 即它說,“在這個范圍內, value標識符指向傳遞的東西。” 該作用域在函數執行結束時消失,除非有理由讓它停留。

如果您從作用域內返回一個函數,那么這就是“作用域表”的一部分閑置的一個很好的理由——當您稍后調用它時,您返回的那個函數可以引用該作用域表中的內容。 出於這個原因,當fun是內創建get_fun的Python告訴funget_fun的范圍表,其中fun保持方便在需要時進行。

您可以在有關執行模型Python 文檔中閱讀有關詳細信息和技術術語(我稍微軟化了一些)的更多信息。 您還可以使用print fun.__closure__函數引用的封閉范圍的部分。 在上面,我們看到對value的引用,它恰好是一個 int:

# Same as before, commented out for emphasis.
#functions = []
#for number in range(1, 6):
#    functions.append(get_fun(number))
#assert [fun() for fun in functions] == range(1, 6)
print functions[0].__closure__
# Produces: (<cell at 0x8dc30: int object at 0x1004188>,)

嘗試使用 () 而不是 []:

listOfLambdas = (lambda: square(i) for i in listOfNumbers)

你會得到:

1
4
9
16
25
listOfLambdas = [lambda i=i: square(i) for i in listOfNumbers]

或者

listOfLambdas = map(lambda i: lambda: square(i), listOfNumbers)

我有時發現為函數對象定義實際的類可以更容易理解發生了什么:

>>> class square(object):
...   def __init__(self, val):
...     self.val = val
...   def __call__(self):
...     return self.val * self.val
...
>>> l = [1,2,3,4,5]
>>> funcs = [square(i) for i in l]
>>> for f in funcs:
...   print f()
...
1
4
9
16
25
>>>

誠然,它比使用 lambda 或閉包更冗長,但我發現當我試圖用函數做不明顯的事情時,這更容易理解。

這將解決您的問題:

import copy

listOfNumbers = [1,2,3,4,5]
square = lambda x: x * x
listOfLambdas = [lambda num=copy.deepcopy(i): square(num) for i in 
listOfNumbers]

for f in listOfLambdas:
    print( f())

你也可以這樣做:

>>> def squares():
...     for i in [1,2,3,4,5]:
...         yield lambda:i*i
... 
>>> print [square() for square in squares()]
[1, 4, 9, 16, 25]

作為附加評論,我想概述從 sympy 矩陣生成 lambda 函數列表的可能性(我不知道這是否是最好的方法,但我就是這樣做的,我覺得很方便):

import sympy as sp
sp.var('Ksi')
# generate sympy expressions for Berstein's polynomials
B_expr = sp.Matrix([sp.binomial(3, i) * Ksi**i * (1 - Ksi)**(3-i) for i in range(4)])
# lambdify them 
B = [sp.lambdify((Ksi), B_expr[i]) for i in range(4) ]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM