在Python中使用语法糖进行函数合成是一个好主意吗?

在Python中使用语法糖进行函数合成是一个好主意吗?,python,function-composition,Python,Function Composition,不久前,我查看了Haskell文档,发现它的函数复合操作符非常好。所以我实现了这个小小的装饰器: from functools import partial class _compfunc(partial): def __lshift__(self, y): f = lambda *args, **kwargs: self.func(y(*args, **kwargs)) return _compfunc(f) def __rshift__(

不久前,我查看了Haskell文档,发现它的函数复合操作符非常好。所以我实现了这个小小的装饰器:

from functools import partial

class _compfunc(partial):
    def __lshift__(self, y):
        f = lambda *args, **kwargs: self.func(y(*args, **kwargs)) 
        return _compfunc(f)

    def __rshift__(self, y):
        f = lambda *args, **kwargs: y(self.func(*args, **kwargs)) 
        return _compfunc(f)

def composable(f):
    return _compfunc(f)

@composable    
def f1(x):
    return x * 2

@composable
def f2(x):
    return  x + 3

@composable
def f3(x):
    return (-1) * x

@composable
def f4(a):
    return a + [0]

print (f1 >> f2 >> f3)(3) #-9
print (f4 >> f1)([1, 2]) #[1, 2, 0, 1, 2, 0]
print (f4 << f1)([1, 2]) #[1, 2, 1, 2, 0]
问题是:
它有用吗?它值得在python邮件列表中讨论吗?

IMHO:不,不值得。虽然我喜欢Haskell,但它似乎不适合Python。除了
(f1>>f2>>f3)
你可以做
组合(f1,f2,f3)
,这就解决了你的问题——你可以将它与任何可调用的东西一起使用,而不需要任何重载、修饰或更改核心(IIRC有人已经提出了
functtools.compose
至少一次;我现在找不到它)


此外,语言定义现在已经冻结,因此他们可能会拒绝这种更改——请参阅。

函数组合在Python中不是一种非常常见的操作,尤其是在显然需要组合运算符的情况下。如果添加了一些内容,我不确定我是否喜欢为Python选择
,这对我来说并不像对您看起来那么明显

我怀疑很多人会更喜欢函数
compose
,它的顺序没有问题:
compose(f,g)(x)
的意思是
f(g(x))
,与数学中的
o
和Haskell中的
顺序相同。Python尽量避免使用标点符号,尤其是当特殊字符没有广为人知的含义时。(对于那些似乎太有用而不能忽略的东西,也有例外,例如@for decorators(非常犹豫)和*和**for function arguments。)


如果您选择将其发送给python ideas,那么如果您可以在stdlib或流行的python库中找到一些实例,函数组合可以使代码更加清晰、易于编写、可维护或高效,那么您可能会赢得更多的用户

您可以使用
reduce
,尽管调用顺序仅为从左到右:

def f1(a):
返回a+1
def f2(a):
返回+10
def f3(a):
返回+100
def呼叫(a,f):
返回f(a)
减少(调用(f1、f2、f3)、5)
#5->f1->f2->f3->116
减少(调用,((λx:x+3),abs),2)
# 5

我没有足够的Python经验来判断语言更改是否值得。但我想描述一下当前语言中可用的选项

为了避免产生意外的行为,函数组合最好遵循标准的数学(或Haskell)操作顺序,即
f∘ G∘ h
应表示应用
h
,然后是
g
,然后是
f


如果您想在Python中使用现有的运算符,那么说
compose()就不太清楚了。作文的方向是什么?您必须查看文档才能回答此问题。@si14:不知道Haskell我找不到>>和@Nikow:我也不知道,但>>和>>f2>>f3表示“将f1的结果放到f2,然后将f2的结果放到f3”。另一种选择是f1(f2(f3(x)),上面有很多凌乱的大括号。@si14:我明白其中的逻辑,但仍然认为这主要是习惯的问题。至少有一种函数式语言过度使用括号;-)对我来说,这听起来类似于(Java-)人抱怨len(a)不是面向对象的,应该是a.len()。这是
>=
的一个特例,它包含一种函数组合,但也从Monad解包@si14:事实上,我假设你的意思是f1>>f2是λx:f2(f1(x))。我现在明白了为什么你要这样解释它了,但这与数学上熟悉的复合运算符的顺序相反……但仍然可以这样写:print(composable(lambda x:x+3)>>composable(abs))(-4)用于数学函数,通常用于*合成和+函数求和,使得(f*g+h)(x)=f(g(x))+h(x)。此外,还可以使用幂运算符:f**2(x)=f(f(x))等。请您澄清一下为什么
>
@max,我不知道如何描述为什么有些东西不明显。没有理由让我看着
说,“啊,应该用它们来做这个!”@max尝试一下:这不太明显,因为没有这种用法的传统。另一个不太明显的原因是,在扩展打印语句(“打印V形”)中使用了不同意义的
>
。对所有人来说,这也不明显:不清楚这会取消哪些类型的对象的资格。@max:也:。这个显式的程度是什么:
(f1>>f2>f4)(3)
?@naxa,这不明显,但我说不出它隐含了什么。对,但这不是中缀(即,
#5->f1->f2->f3->116
),为了澄清,在引用的框架中,每个函数都必须定义为
中缀操作符,而不是像Mathematica这样的langs(和其他),其中可以在不重新定义的情况下组合函数。
((lambda x: x + 3) >> abs)(2)