У меня есть некоторые предикаты, например ::100100
is_divisible_by_13 = lambda i: i % 13 == 0
is_palindrome = lambda x: str(x) == str(x)[::-1]
и хотите логически объединить их, как в:
filter(lambda x: is_divisible_by_13(x) and is_palindrome(x), range(1000,10000))
Вопрос теперь: можно ли написать такую комбинацию в стиле pointfree , например:
filter(is_divisible_by_13 and is_palindrome, range(1000,10000))
Это, конечно, не желаемый эффект, потому что истинное значение лямбда-функций равно 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
С помощью P
теперь я могу создать новый предикат, который представляет собой комбинацию предикатов, таких как:
P(is_divisible_by_13).and_(is_palindrome)
, что эквивалентно вышеуказанной лямбда-функции. Это ближе к тому, что я хотел бы иметь, но это также не бессмысленно (точки теперь являются самими предикатами, а не их аргументами). Теперь второй вопрос: есть ли лучший или более короткий способ (возможно, без скобок и точек) комбинировать предикаты в Python, чем использовать классы, такие как P
и без использования (лямбда) функций?