在Python中给函数组合添加语法糖是否是个好主意?

19 投票
4 回答
1751 浏览
提问于 2025-04-15 19:24

前一段时间,我查看了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]

问题是: 如果没有语言的支持,我们就不能像这样在内置函数或匿名函数(也叫lambda)上使用这种语法:

((lambda x: x + 3) >> abs)(2)

那么,问题来了: 这个东西有用吗?值得在Python邮件列表上讨论吗?

4 个回答

6

你可以使用 reduce 来实现这个功能,不过要注意,调用的顺序只能是从左到右。

def f1(a):
    return a+1

def f2(a):
    return a+10

def f3(a):
    return a+100

def call(a,f):
    return f(a)


reduce(call, (f1, f2, f3), 5)
# 5 -> f1 -> f2 -> f3 -> 116
reduce(call, ((lambda x: x+3), abs), 2)
# 5
6

函数组合在Python中并不是一个特别常见的操作,尤其是没有一个明显需要的组合运算符。如果要添加什么东西,我不太确定我喜欢用<<>>作为Python的选择,因为对我来说,这并没有那么明显,可能对你来说就简单多了。

我觉得很多人可能会更喜欢一个叫compose的函数,使用起来也不会有问题:compose(f, g)(x)的意思是f(g(x)),这个顺序和数学中的o以及Haskell中的.是一样的。Python尽量避免使用标点符号,而是用英语单词来表达,特别是当这些特殊字符没有广为人知的含义时。(当然,有些例外,比如@用于装饰器(虽然很犹豫)以及*和**用于函数参数。)

如果你决定把这个发给python-ideas,可能会吸引更多人关注,如果你能找到一些在标准库或流行的Python库中,函数组合能让代码更清晰、易于编写、易于维护或更高效的例子,那就更好了。

8

在我看来:不是的。这种写法虽然我喜欢 Haskell,但在 Python 里似乎不太合适。与其用 (f1 >> f2 >> f3) 这种方式,不如用 compose(f1, f2, f3) 来解决问题——这样你可以用它来处理任何可调用的对象,而不需要重载、装饰或者改变核心功能(如果我没记错的话,之前有人提过 functools.compose;不过我现在找不到相关信息了)。

另外,现在语言的定义已经定下来了,所以他们可能会拒绝这种改变——可以看看 PEP 3003

撰写回答