让一元运算符使用Python类

时间:2017-12-28 08:13:14

标签: python metaclass

代码:

>>> 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

其他一元运算符及其相关的“神奇”方法也是如此(例如~__invert__+__pos__等。

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

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

class Negative: pass

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

class Positive(metaclass=PositiveMeta): pass

3 个答案:

答案 0 :(得分:4)

您的代码无法像最初编写的那样工作的原因是您无法在实例中定义魔术方法。根据{{​​3}}:

  

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

这适用于类(它是某些元类的实例),就像它对#34;常规"一样。对象。从这个意义上说,这个问题等同于以下任何一个问题:docsOverriding special methods on an instanceWhy does Python's bool builtin only look at the class-level __bool__ method

使用@classmethod修饰魔术方法与将__get__获取的绑定方法分配给实例是一样的。在这两种情况下,Python都会忽略任何未在类中定义的描述符。

这也是-Positive() is Negative起作用的原因。当您取消Positive的实例时,解释器会在类中查找__neg__。使用@classmethod进行装饰在这里完全是多余的,因为无论如何都忽略了输入参数。但是现在你有了一个返回类对象的魔术方法。

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

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

class Negative: pass

class Positive(metaclass=MetaPositive): pass

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

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

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

问题仍然存在,就像往常一样,为什么你想要首先做这样的事情。

答案 1 :(得分:3)

这似乎有效:

class Negative: pass

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

class Positive(metaclass=PositiveMeta):
    pass

print(-Positive is Negative)  # prints True

答案 2 :(得分:3)

试试这个:

class Negative:
    pass

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

class Positive(metaclass=meta):
    pass

-Positive
#output __main__.Negative