19

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

问题:如果没有语言支持,我们不能在内置函数或 lambdas 上使用这种语法,如下所示:

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

问题:有用吗?值得在 python 邮件列表上讨论吗?

4

4 回答 4

8

恕我直言:不,不是。虽然我喜欢 Haskell,但这似乎不适合 Python。而不是(f1 >> f2 >> f3)你可以这样做compose(f1, f2, f3),这可以解决你的问题——你可以将它与任何可调用对象一起使用,而无需任何重载、装饰或更改核心(IIRC 有人已经functools.compose至少提出过一次;我现在找不到它)。

此外,语言定义现在被冻结,所以他们可能无论如何都会拒绝这种改变——见PEP 3003

于 2010-02-17T15:20:30.993 回答
6

函数组合在 Python 中并不是一个超级常见的操作,尤其是在显然需要组合运算符的情况下。如果添加了某些内容,我不确定我是否喜欢 Python 的选择<<>>用于 Python,这对我来说并不像对您而言那么明显。

我怀疑很多人会更喜欢 function compose,它的顺序没有问题:compose(f, g)(x)这意味着,与数学和Haskell中f(g(x))的顺序相同。Python 尽量避免在英语单词可以使用的情况下使用标点符号,尤其是当特殊字符没有广为人知的含义时。(对于看起来太有用而不能忽略的东西,例如 @ 用于装饰器(犹豫不决)和 * 和 ** 用于函数参数。)o.

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

于 2010-02-17T17:54:59.937 回答
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
于 2011-10-07T14:31:46.140 回答
1

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

为避免产生意外行为,函数组合理想情况下应遵循标准数学(或 Haskell)操作顺序,即f ∘ g ∘ h应表示 apply h、 then g、 then f

如果您想在 Python 中使用现有的运算符,例如<<,正如您提到的,您会遇到 lambda 和内置函数的问题。__rlshift__除了__lshift__. _ 这样,与对象相邻的 lambda/built-inscomposable将得到处理。当您确实有两个相邻的 lambda/built-ins 时,您需要composable像 @si14 建议的那样使用 显式转换(只是其中一个)。注意我的意思是__rlshift__,不是__rshift__;事实上,我建议__rshift__完全不要使用,因为尽管操作符的形状提供了方向提示,但顺序变化令人困惑。

但是您可能需要考虑另一种方法。Ferdinand Jamitzky 有一个很好的秘诀,可以在 Python 中定义伪中缀运算符,甚至可以在内置函数上使用。有了这个,你就可以编写f |o| g函数组合了,实际上看起来很合理。

于 2012-12-05T16:48:40.417 回答