让一元运算符与 Python 类 一起工作

Getting unary operators to work with Python classes

代码:

>>> class Negative: 
...      pass

>>> class Positive:
...    @classmethod
...    def __neg__(cls):
...        return Negative

所以我试试

>>> -Positive is Negative
TypeError: bad operand type for unary -: 'type'

这虽然有效

>>> -Positive() is Negative
True

其他一元运算符及其相关的 "magic" 方法(例如 ~__invert__+__pos__ 等)也是如此。

为什么它适用于实例而不适用于 类?我怎样才能让它工作?

编辑:我已经按照建议修改了代码,以在元类中移动魔法方法。

class Negative: pass

class PositiveMeta(type):
    def __neg__(cls):
        return Negative

class Positive(metaclass=PositiveMeta): pass

这似乎有效:

class Negative: pass

class PositiveMeta(type):
    def __neg__(self):
        return Negative

class Positive(metaclass=PositiveMeta):
    pass

print(-Positive is Negative)  # prints True

试试这个:

class Negative:
    pass

class meta(type):
    def __neg__(cls):
        return Negative

class Positive(metaclass=meta):
    pass

-Positive
#output __main__.Negative

你的代码不能像最初写的那样工作的原因是你不能在实例中定义魔术方法。根据 docs:

For custom classes, implicit invocations of special methods are only guaranteed to work correctly if defined on an object’s type, not in the object’s instance dictionary.

这适用于 classes(它们是某些元class 的实例),就像它对 "regular" 对象一样。从这个意义上说,这个问题等同于以下任何问题:Overriding special methods on an instance, , .

@classmethod 装饰你的魔法方法类似于将通过 __get__ 获得的绑定方法分配给一个实例。在这两种情况下,Python 都会简单地忽略任何未在 class.

中定义的描述符

这也是 -Positive() is Negative 起作用的原因。当您否定 Positive 的实例时,解释器会在 class 中查找 __neg__。用 @classmethod 装饰在这里完全是多余的,因为您无论如何都会忽略输入参数。但是现在你有一个神奇的方法 returns 一个 class 对象。

要在您的 class 对象上正确定义魔术方法,您需要在 metaclass:

上定义它
class MetaPositive(type):
    def __neg__(self):
        return Negative

class Negative: pass

class Positive(metaclass=MetaPositive): pass

有趣的是,这并不局限于一元运算符。您可以在 metaclass 上定义 any dunder 方法,并让您的 classes 支持相应的操作:

class MetaPositive(type):
    def __gt__(self, other):
        if other is Negative:
            return True
        return False

现在您可以使用 > 运算符来比较您的 classes。我并不是暗示你应该做那样的事情,但这种可能性肯定存在。

与往常一样,问题仍然存在,即您为什么首先要这样做。