5

作为一个人为的例子,假设我在 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)

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

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'

关于如何使这项工作的任何想法?

4

2 回答 2

5

__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'>)
于 2013-02-21T17:57:54.653 回答
1

您不能将特殊(挂钩)方法作为类方法添加到类中,因为它们总是在当前对象的类型上查找;对于类上的实例,对于类,它们会被查找type。有关为什么会这样的动机,请参阅这个先前的答案

这意味着您需要在元类上实现它;元类充当类的类型:

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
于 2013-02-21T17:48:00.583 回答