python函数比较dunders的使用

2024-09-30 01:26:51 发布

您现在位置:Python中文网/ 问答频道 /正文

问题:

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


Tags: lambda函数代码ltgtreturnfoodef
2条回答

Yet, curiouser and curiouser, this doesn't work:

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

how can one use them, and how should they be used.

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

it could be used to construct a pipeline.

啊,啊,啊

这在很大程度上与Python的禅宗格格不入。有一些原因表明它还不能以这种方式工作。Numpy对运营商所做的事情已经在推动它,而且大多数情况下是可以容忍的,因为它们是多么的有用。(另外,正如在评论中指出的,Python对链式关系运算符的特殊处理将使它无法按您希望的方式在非平凡情况下工作。)

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

# 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

相关问题 更多 >

    热门问题