首页 > 解决方案 > python函数比较dunders的使用

问题描述

问题

Python 函数有比较 dunders(见下面的打印输出)。但他们是NotImplemented。很公平。但是它们的预期用途是什么,如何使用它们?当我分配一个可调用对象时func.__gt__,我没有看到它被调用func < other_func

示例代码

我可以看到使用与(foo > bar)等效的函数lambda x: foo(x) > bar(x),但同样(并且可以说更有用),它可以用于构造管道。

例如,我们可以有

def composeable(func):
    func.__gt__ = lambda g: lambda x: g(f(x))
    func.__lt__ = lambda g: lambda x: f(g(x))
    return func

可以用作

>>> def composeable(func):
...     func.__gt__ = lambda g: lambda x: g(f(x))
...     func.__lt__ = lambda g: lambda x: f(g(x))
...     return func
...
>>> @composeable
... def f(x):
...     return x + 2
...
>>> def g(x):
...     return x * 10
...
>>> h = f.__gt__(g)
>>> assert h(3) == 50  # (3 + 2) * 10
>>> h = f.__lt__(g)
>>> assert h(3) == 32  # (3 * 10) + 2

然而,越来越好奇,这行不通:

>>> h = f > g
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '>' not supported between instances of 'function' and 'function'

注意:可调用NotImplemented函数 dunders。

__eq__: NotImplemented
__ge__: NotImplemented
__gt__: NotImplemented
__le__: NotImplemented
__lt__: NotImplemented
__ne__: NotImplemented

生成上述打印输出的代码:

from inspect import signature

def f(x): ...

for aname in dir(f):
    attr = getattr(f, aname)
    if callable(attr):
        try:
            x = attr(*len(signature(attr).parameters) * [''])
            if x is NotImplemented:
                print(f"{aname}: NotImplemented")
        except Exception as e:
            pass

标签: pythonpython-3.xpython-datamodel

解决方案


您可以做的最简单的事情是定义一个支持组合的包装器。由于比较运算符的处理,我建议使用>>and<<进行组合。

# Assumption: the wrapped callable take a single argument
class ComposableCallable:
    def __init__(self, func):
        self.func = func

    def __lshift__(self, other):
        @ComposableCallable
        def _(x):
            return self.func(other.func(x))

        return _

    def __rshift__(self, other):
        @ComposableCallable
        def _(x):
            return other.func(self.func(x))

        return _

    def __call__(self, x):
        return self.func(x)


@ComposableCallable
def f(x):
    return x + 1

@ComposableCallable
def g(x):
    return 2 * x


assert (f << g)(3) == 7  # 2*3 + 1 == 7
assert (f >> g)(3) == 8  # (3 + 1) * 2 == 8

推荐阅读