【问题标题】:Getting unary operators to work with Python classes让一元运算符与 Python 类一起工作
【发布时间】:2018-06-08 19:01:57
【问题描述】:

代码:

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

【问题讨论】:

  • 类的类是它的元类。在自定义元类中定义方法,而不是在类方法中。魔术方法只有在类中定义时才会被识别。
  • @Jean-FrançoisFabre 为什么不呢?他们只需要进入一个元类才能生效。
  • 事实上,这可能并不局限于一元运算符。如果您在元类中定义适当的方法,您可能可以将类添加和相乘。
  • @MadPhysicist:是的,它有效。我认为我的问题是我最初将 Python2 元类语法与 Python3 混合在一起。谢谢
  • 不错。 Python 面向对象的绝对威严从未停止让我惊讶。 Go apeshit:为你的类定义一些二元运算符。让它们相互除以指数。

标签: python metaclass


【解决方案1】:

您的代码无法按照最初编写的方式运行的原因是您无法在实例中定义魔术方法。根据docs

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

这适用于类(它们是某些元类的实例),就像它适用于“常规”对象一样。从这个意义上说,这个问题等同于以下任何一个问题:Overriding special methods on an instanceWhy does Python's bool builtin only look at the class-level __bool__ methodAssigning (instead of defining) a __getitem__ magic method breaks indexing

@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

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

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

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

问题仍然存在,就像它经常发生的那样,你为什么要首先做这样的事情。

【讨论】:

  • 感谢更完整的解释。我移动了已接受的答案,因为您的评论最初使我重回正轨。事实上,我已经尝试通过元类来执行此操作,但是作为 Python3 的新手,我没有意识到旧语法 (__metaclass__ = PositiveMeta) 没有做任何事情,并且错误消息让我失望了(操作类型错误)。你们的 cmets 证实我的一些假设是正确的,而另一些则需要重新检查。很好,谢谢。
  • @mike_e。好电话,我的答案只适用于 Python 3。感谢您选择它。虽然我同意它提供了更详细的解释,但我也必须赞扬其他答案。他们证实了我最初只是一个非常有根据的猜测,是在没有翻译的移动设备上做出的。
【解决方案2】:

试试这个:

class Negative:
    pass

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

class Positive(metaclass=meta):
    pass

-Positive
#output __main__.Negative

【讨论】:

    【解决方案3】:

    这似乎有效:

    class Negative: pass
    
    class PositiveMeta(type):
        def __neg__(self):
            return Negative
    
    class Positive(metaclass=PositiveMeta):
        pass
    
    print(-Positive is Negative)  # prints True
    

    【讨论】:

    • 为什么你的例子中有一个类方法?
    • 我的意思是,你为什么要在你展示了定义__neg__的正确方法之后,再费心定义__pos__
    • 它是从 OP 的代码中复制而来的。但是,它不是必需的,并且可能会使未来的读者感到困惑,因此我编辑了我的答案。
    • 这完全适得其反。回答问题的重点是修复 OP 的代码,而不是让它更复杂。
    猜你喜欢
    • 1970-01-01
    • 2017-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多