Python中的Pointfree函数组合

Fra*_*mas 9 python predicate pointfree function-composition

我有一些谓词,例如:

is_divisible_by_13 = lambda i: i % 13 == 0
is_palindrome = lambda x: str(x) == str(x)[::-1]
Run Code Online (Sandbox Code Playgroud)

并希望在逻辑上将它们组合在一起,如:

filter(lambda x: is_divisible_by_13(x) and is_palindrome(x), range(1000,10000))
Run Code Online (Sandbox Code Playgroud)

现在的问题是:这种组合可以用无样式编写,例如:

filter(is_divisible_by_13 and is_palindrome, range(1000,10000))
Run Code Online (Sandbox Code Playgroud)

这当然不具有期望的效果,因为λ函数的真值是Trueand并且or是短路运算符.我想出的最接近的事情是定义一个类P,它是一个简单的谓词容器,它实现__call__()并拥有方法and_()or_()组合谓词.定义P如下:

import copy

class P(object):
    def __init__(self, predicate):
        self.pred = predicate

    def __call__(self, obj):
        return self.pred(obj)

    def __copy_pred(self):
        return copy.copy(self.pred)

    def and_(self, predicate):
        pred = self.__copy_pred()
        self.pred = lambda x: pred(x) and predicate(x)
        return self

    def or_(self, predicate):
        pred = self.__copy_pred()
        self.pred = lambda x: pred(x) or predicate(x)
        return self
Run Code Online (Sandbox Code Playgroud)

随着P我现在可以创建一个新的谓词是这样的谓词的组合:

P(is_divisible_by_13).and_(is_palindrome)
Run Code Online (Sandbox Code Playgroud)

这相当于上面的lambda函数.这更接近我想要的,但它也不是无点的(点现在是谓词本身而不是他们的论点).现在第二个问题是:是否有更好或更短的方式(可能没有括号和点)组合Python中的谓词而不是使用类P和不使用(lambda)函数的类?

Tam*_*más 9

您可以&通过向类添加__and__方法来覆盖Python中的(按位AND)运算符P.然后你可以写下这样的东西:

P(is_divisible_by_13) & P(is_palindrome)
Run Code Online (Sandbox Code Playgroud)

甚至

P(is_divisible_by_13) & is_palindrome
Run Code Online (Sandbox Code Playgroud)

类似地,您可以|通过添加__or__方法来覆盖(按位OR)运算符,并通过添加方法来添加~(按位取反)运算符__not__.需要注意的是,你不能覆盖内置的and,ornot运营商,所以这可能是接近你的目标成为可能.您仍然需要将P实例作为最左侧的参数.

为了完整起见,您还可以覆盖这些运算符的就地变体(__iand__,__ior__)和右侧变体(__rand__,__ror__).

代码示例(未经测试,随意更正):

class P(object):
    def __init__(self, predicate):
        self.pred = predicate

    def __call__(self, obj):
        return self.pred(obj)

    def __copy_pred(self):
        return copy.copy(self.pred)

    def __and__(self, predicate):
        def func(obj):
            return self.pred(obj) and predicate(obj)
        return P(func)

    def __or__(self, predicate):
        def func(obj):
            return self.pred(obj) or predicate(obj)
        return P(func)
Run Code Online (Sandbox Code Playgroud)

让你更接近无点天堂的另一个技巧是以下装饰:

from functools import update_wrapper

def predicate(func):
    """Decorator that constructs a predicate (``P``) instance from
    the given function."""
    result = P(func)
    update_wrapper(result, func)
    return result
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用predicate装饰器标记谓词,使它们成为P自动的实例:

@predicate
def is_divisible_by_13(number):
    return number % 13 == 0

@predicate
def is_palindrome(number):
    return str(number) == str(number)[::-1]

>>> pred = (is_divisible_by_13 & is_palindrome)
>>> print [x for x in xrange(1, 1000) if pred(x)]
[494, 585, 676, 767, 858, 949]
Run Code Online (Sandbox Code Playgroud)