在Python中给函数组合添加语法糖是否是个好主意?
前一段时间,我查看了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 个回答
你可以使用 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
函数组合在Python中并不是一个特别常见的操作,尤其是没有一个明显需要的组合运算符。如果要添加什么东西,我不太确定我喜欢用<<
和>>
作为Python的选择,因为对我来说,这并没有那么明显,可能对你来说就简单多了。
我觉得很多人可能会更喜欢一个叫compose
的函数,使用起来也不会有问题:compose(f, g)(x)
的意思是f(g(x))
,这个顺序和数学中的o
以及Haskell中的.
是一样的。Python尽量避免使用标点符号,而是用英语单词来表达,特别是当这些特殊字符没有广为人知的含义时。(当然,有些例外,比如@用于装饰器(虽然很犹豫)以及*和**用于函数参数。)
如果你决定把这个发给python-ideas,可能会吸引更多人关注,如果你能找到一些在标准库或流行的Python库中,函数组合能让代码更清晰、易于编写、易于维护或更高效的例子,那就更好了。
在我看来:不是的。这种写法虽然我喜欢 Haskell,但在 Python 里似乎不太合适。与其用 (f1 >> f2 >> f3)
这种方式,不如用 compose(f1, f2, f3)
来解决问题——这样你可以用它来处理任何可调用的对象,而不需要重载、装饰或者改变核心功能(如果我没记错的话,之前有人提过 functools.compose
;不过我现在找不到相关信息了)。
另外,现在语言的定义已经定下来了,所以他们可能会拒绝这种改变——可以看看 PEP 3003。