2013-02-21 57 views
4

作爲一個人爲的例子,假設我在python中生成一個隨機水果籃。我創建籃子:覆蓋python類上的__or__運算符

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' 

任何思考如何做這個工作?

回答

3

__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'>) 
+0

正是我一直在試圖找到!謝謝!很棒 – 2013-02-21 18:00:37

1

不能添加特殊(鉤)方法作爲類方法的類,因爲他們總是擡頭對當前對象的類型;對於課堂上的實例,對於課程,它們是在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