【问题标题】:Overriding __or__ operator on python classes在 python 类上覆盖 __or__ 运算符
【发布时间】:2013-02-21 17:40:25
【问题描述】:

作为一个人为的例子,假设我在 python 中生成一个随机的水果篮。我创建了篮子:

basket = FruitBasket()

现在我想指定篮子中可能出现的特定水果组合。假设我是一个非常挑剔的家伙,篮子要么必须装满苹果和石榴、橙子和葡萄柚,要么只有香蕉。

我正在阅读有关 python 运算符重载的内容,似乎我可以定义 __or____and__ 来获得我想要的行为。我想我可以这样做:

basket.fruits = (Apple() & Pomegranate()) | (Banana()) | (Orange() & Grapefruit())

创建两个类(OrAnd)就可以了。当__or____and__ 被调用时,我只需返回一个新的OrAnd 对象:

def __or__(self, other):
    return Or(self, other)

def __and__(self, other):
    return And(self, other)

我想弄清楚的是如何在不必先实例化水果的情况下做到这一点?为什么我不能在基础 Fruit 类上使用静态 __or__ 方法?我已经尝试过了,但它不起作用:

class Fruit(object):
    @classmethod
    def __or__(self, other):
        return Or(self, other)

并分配水果:

basket.fruits = (Apple & Pomegranate) | (Orange & Grapefruit) | (Banana)

我收到这样的错误:

TypeError: unsupported operand type(s) for |: 'type' and 'type'

对如何进行这项工作有任何想法吗?

【问题讨论】:

    标签: python operator-overloading


    【解决方案1】:

    __or__在对象的类型上查找;对于Fruit 实例,将是Fruit;对于Fruit,即type。不过,您可以通过使用元类来更改 Fruit 的类型:

    class FruitMeta(type):
    
        def __or__(self, other):
            return Or(self, other)
    
    
    class Fruit(object):
        __metaclass__ = FruitMeta
    

    (对于 Python 3,语法改为 class Fruit(metaclass=FruitMeta):。)

    然后,这可以满足您的所有需求。 Apple | Banana(假设这两个是Fruit 的子类)将产生Or(Apple, Banana)

    不过,这种设计要非常小心。它趋于魔法领域,很容易引起混乱。

    (完整的演示,在 Python 2.7 中:)

    >>> class Or(object):
    ...     def __init__(self, a, b):
    ...             self.a = a
    ...             self.b = b
    ...     def __repr__(self):
    ...             return 'Or({!r}, {!r})'.format(self.a, self.b)
    ... 
    >>> class FruitMeta(type):
    ...     def __or__(self, other):
    ...             return Or(self, other)
    ... 
    >>> class Fruit(object):
    ...     __metaclass__ = FruitMeta
    ... 
    >>> class Apple(Fruit): pass
    ... 
    >>> class Banana(Fruit): pass
    ... 
    >>> Apple | Banana
    Or(<class '__main__.Apple'>, <class '__main__.Banana'>)
    

    【讨论】:

    • 正是我想要找到的!谢谢!效果很好
    【解决方案2】:

    您不能将特殊(挂钩)方法作为类方法添加到类中,因为它们总是在当前对象的类型上查找;对于类上的实例,对于类,它们会在 type 上查找。请参阅this previous answer 了解原因。

    这意味着您需要在metaclass 上实现此功能;元类充当类的类型:

    class FruitMeta(type):
        def __or__(cls, other):
            return Or(cls, other)
    
        def __and__(cls, other):
            return And(cls, other)
    

    那么对于 Python 3:

    class Fruit(metaclass=FruitMeta):
    

    或 Python 2:

    class Fruit(object):
        __metaclass__ = FruitMeta
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-06-23
      • 2011-01-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-09
      相关资源
      最近更新 更多