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