簡體   English   中英

這個特定的Python函數組合背后的邏輯是什么?

[英]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)

我有兩個問題:

  1. 有人可以向我解釋compose “操作邏輯”嗎? (這個怎么運作?)
  2. 是否可以(以及如何?)在不使用reduce情況下獲得相同的東西?

我已經看到這里這里這里過 ,我的問題是沒有理解什么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。

因此,序列中的前兩個函數是sincos ,它們被替換為:

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.

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