让一元运算符与 Python 类一起使用

Mic*_*oka 5 python metaclass

代码:

>>> class Negative: 
...      pass

>>> class Positive:
...    @classmethod
...    def __neg__(cls):
...        return Negative
Run Code Online (Sandbox Code Playgroud)

所以我尝试

>>> -Positive is Negative
TypeError: bad operand type for unary -: 'type'
Run Code Online (Sandbox Code Playgroud)

这虽然有效

>>> -Positive() is Negative
True
Run Code Online (Sandbox Code Playgroud)

~其他一元运算符及其相关的“魔术”方法(例如and __invert__+and__pos__等)也是如此。

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

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

class Negative: pass

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

class Positive(metaclass=PositiveMeta): pass
Run Code Online (Sandbox Code Playgroud)

Mad*_*ist 5

您的代码无法按最初编写的方式工作的原因是您无法在实例中定义魔术方法。根据文档

\n\n
\n

对于自定义类,只有在 object\xe2\x80\x99s 类型上定义特殊方法的隐式调用才能保证正确工作,而不是在 object\xe2\x80\x99s 实例字典中定义。

\n
\n\n

这适用于类(它们是某些元类的实例),就像它适用于“常规”对象一样。从这个意义上说,这个问题相当于以下任何一个问题:重写实例上的特殊方法为什么Python的bool内置函数只查看类级别的__bool__方法分配(而不是定义)一个__getitem__魔术方法会破坏索引

\n\n

装饰你的魔术方法@classmethod类似于将通过实例获得的绑定方法分配__get__给实例。在这两种情况下,Python 都会忽略类中未定义的任何描述符。

\n\n

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

\n\n

要在类对象上正确定义魔术方法,您需要在元类上定义它:

\n\n
class MetaPositive(type):\n    def __neg__(self):\n        return Negative\n\nclass Negative: pass\n\nclass Positive(metaclass=MetaPositive): pass\n
Run Code Online (Sandbox Code Playgroud)\n\n

有趣的是,这并不限于一元运算符。您可以在元类上定义任何dunder 方法,并使您的类支持相应的操作:

\n\n
class MetaPositive(type):\n    def __gt__(self, other):\n        if other is Negative:\n            return True\n        return False\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

正如经常出现的那样,问题仍然是,为什么你首先想要做这样的事情。

\n