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)
这当然不具有期望的效果,因为λ函数的真值是True和and并且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)函数的类?
您可以&通过向类添加__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,or和not运营商,所以这可能是接近你的目标成为可能.您仍然需要将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)