[英]What's the logic behind this particular Python functions composition?
請考慮以下有關函數組合的Python代碼段:
from functools import reduce
def compose(*funcs):
# compose a group of functions into a single composite (f(g(h(..(x)..)))
return reduce(lambda f, g: lambda *args, **kwargs: f(g(*args, **kwargs)), funcs)
### --- usage example:
from math import sin, cos, sqrt
mycompositefunc = compose(sin,cos,sqrt)
mycompositefunc(2)
我有兩個問題:
我已經看到這里 , 這里和這里過 ,我的問題是沒有理解什么lambda
手段或reduce
不會(我覺得我得到的,例如,這2
在使用例如將有所第一要素funcs
被組成)。 我發現難以理解的是兩個lambda
如何組合/嵌套並與*args, **kwargs
混合的復雜性*args, **kwargs
在這里作為reduce
第一個參數...
編輯:
首先,@ Martijn和@Borealid,感謝您的努力和答案以及您獻給我的時間。 (抱歉延誤,我在業余時間這樣做,並不總是有很多......)
好的,現在來看看事實......
關於我的問題的第1點:
在此之前,我意識到我沒有真正得到的(但我希望我現在做的)關於那些*args, **kwargs
變量參數之前是至少 **kwargs
不是強制性的 (我說得好,對吧?)例如,讓我理解為什么mycompositefunc(2)
只使用一個(非關鍵字)傳遞參數。
然后,我意識到這個例子甚至可以用簡單的x
替換內部lambda中的*args, **args
。 我想這是因為,在這個例子中,所有3個組合函數( sin, cos, sqrt
)都期望一個(和一個唯一的)參數...當然,返回一個結果......所以,更具體地說,它的工作原理因為第一個組合函數只需要一個參數(以下其他函數在這里自然只會得到一個參數,這是以前組合函數的結果,所以你不能組成在第一個參數之后需要多個參數的函數...我知道這有點扭曲,但我認為你得到了我想解釋的......)
現在,我在這里找到真正不明確的問題:
lambda f, g: lambda *args, **kwargs: f(g(*args, **kwargs))
那個lambda嵌套“魔法”是如何工作的?
有了你應得的所有尊重並且我忍受了你,在我看來你們兩個都錯了得出結論,最終結果應該是: sqrt(sin(cos(*args, **kw)))
。 它實際上不可能,sqrt函數的設備順序明顯顛倒過來:它不是最后編寫的,而是第一個。
我這樣說是因為:
>>> mycompositefunc(2)
0.1553124117201235
它的結果等於
>>> sin(cos(sqrt(2)))
0.1553124117201235
而你得到一個錯誤
>>> sqrt(sin(cos(2)))
[...]
ValueError: math domain error
(這是因為試圖將負浮點數設為平方)
#P.S. for completeness:
>>> sqrt(cos(sin(2)))
0.7837731062727799
>>> cos(sin(sqrt(2)))
0.5505562169613818
所以,我明白函數組成將從最后一個到第一個(即:compose(sin,cos,sqrt)=> sin(cos(sqrt(x))))但是“ 為什么? ”以及如何lambda嵌套“魔法”有效嗎? 仍然有點不清楚...幫助/建議非常感謝!
在第2點(關於重寫撰寫沒有減少)
@Martijn Pieters:你的第一個作曲(“包裹”的作品)起作用並返回完全相同的結果
>>> mp_compfunc = mp_compose(sin,cos,sqrt)
>>> mp_compfunc(2)
0.1553124117201235
不幸的是,展開的版本循環直到RuntimeError: maximum recursion depth exceeded
...
@Borealid:你的foo / bar示例不會有兩個以上的函數用於合成,但我認為這只是為了解釋不是為了回答第二點,對吧?
lambda
簽名和調用語法中的*args, **kw
語法是傳遞任意參數的最佳方法。 它們接受任意數量的位置和關鍵字參數,並將這些參數傳遞給下一個調用。 您可以將外部lambda
的結果寫為:
def _anonymous_function_(*args, **kw):
result_of_g = g(*args, **kw)
return f(result_of_g)
return _anonymous_function
可以在沒有reduce()
情況下重寫compose
函數,如下所示:
def compose(*funcs):
wrap = lambda f, g: lambda *args, **kw: f(g(*args, **kw))
result = funcs[0]
for func in funcs[1:]:
result = wrap(result, func)
return result
這與reduce()
調用完全相同; 為函數鏈調用lambda。
因此,序列中的前兩個函數是sin
和cos
,它們被替換為:
lambda *args, **kw: sin(cos(*args, **kw))
然后將其作為f
傳遞給下一個調用,使用sqrt
g
,所以你得到:
lambda *args, **kw: (lambda *args, **kw: sin(cos(*args, **kw)))(sqrt(*args, **kw)))
可以簡化為:
lambda *args, **kw: sin(cos(sqrt(*args, **kw)))
因為f()
是一個lambda,它將其參數傳遞給嵌套的sin(cos())
調用。
最后,您生成了一個調用sqrt()
的函數,其結果傳遞給cos()
,然后將其輸出傳遞給sin()
。 *args, **kw
允許您傳入任意數量的參數或關鍵字參數,因此compose()
函數可以應用於除可調用之外的任何內容 ,前提是除了第一個函數之外的所有函數只需要一個參數。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.