0

问题

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

4

2 回答 2

1

您可以做的最简单的事情是定义一个支持组合的包装器。由于比较运算符的处理,我建议使用>>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
于 2020-05-22T20:35:23.227 回答
0

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

这与 dunder 方法的实现无关,与您试图修改内置函数类型的现有行为有关。您将无法修改基础类,并且忽略在实例上分配这些属性。

如何使用它们,以及应该如何使用它们。

您将它们用于您自己的类,在这些类中,实例之间存在自然比较。例如,标准库fractions.Fraction类支持这些比较;并在 Python 中实现,需要使用这些方法。

它可以用来建造管道。

啊啊啊。

这非常违背 Python 的禅宗。它还没有那样工作是有原因的。恕我直言,Numpy 对运营商所做的事情已经在推动它,并且由于它们非常有用,因此大多被容忍。(此外,正如评论中所指出的,Python 对链式关系运算符的特殊处理将使这对于非平凡的情况无法按照您希望的方式工作。)

于 2020-05-22T20:25:22.180 回答