在 python 类上覆盖 __or__ 运算符 [英] Overriding __or__ operator on python classes
问题描述
举一个人为的例子,假设我在 python 中生成一个随机水果篮.我创建了篮子:
basket = FruitBasket()
现在我想指定篮子中可能出现的特定水果组合.假设我是一个非常挑剔的人,篮子里要么装满苹果和石榴、橙子和葡萄柚,要么只有香蕉.
我正在阅读 python 运算符重载,似乎我可以定义 __or__
和 __and__
来获得我想要的行为.我想我可以做这样的事情:
basket.fruits = (Apple() & Pomegranate()) |(香蕉()) |(橙色()和葡萄柚())
这可以很好地制作两个类(Or
和 And
).当 __or__
或 __and__
被调用时,我只返回一个新的 Or
或 And
对象:
def __or__(self, other):返回或(自己,其他)def __and__(self, other):返回 And(self, other)
我想弄清楚的是如何在不必先实例化水果的情况下做到这一点?为什么我不能在基本 Fruit
类上使用静态 __or__
方法?我试过这个,但它不起作用:
class Fruit(object):@类方法def __or__(self, other):返回或(自己,其他)
并分配水果:
basket.fruits = (Apple & Pomegranate) |(橙子和葡萄柚) |(香蕉)
我收到这样的错误:
TypeError: 不支持的操作数类型 |: 'type' 和 'type'
对如何使这项工作有任何想法?
__or__
在对象的类型上查找;对于 Fruit
实例,这将是 Fruit
;对于Fruit
,即type
.不过,您可以使用元类更改 Fruit
的类型:
class FruitMeta(type):def __or__(self, other):返回或(自己,其他)类水果(对象):__metaclass__ = FruitMeta
(对于 Python 3,语法为 class Fruit(metaclass=FruitMeta):
.)
这就是你想要的.苹果|Banana
(假设这两个是Fruit
的子类)将产生Or(Apple, Banana)
.
不过,要非常小心这种设计.它趋向于魔法领域,很容易引起混乱.
(完整演示,在 Python 2.7 中:)
<预><代码>>>>类或(对象):... def __init__(self, a, b):... self.a = a... self.b = b... def __repr__(self):... return 'Or({!r}, {!r})'.format(self.a, self.b)...>>>类 FruitMeta(类型):... def __or__(self, other):...返回或(自我,其他)...>>>类水果(对象):... __metaclass__ = FruitMeta...>>>类苹果(水果):通过...>>>类香蕉(水果):通过...>>>苹果 |香蕉或(As a contrived example, suppose I'm generating a random fruit basket in python. I create the basket:
basket = FruitBasket()
Now I want to specify specific combinations of fruit that can occur in the basket. Suppose I'm a very picky dude, and the basket either has to be full of apples and pomegranates, oranges and grapefruit, or only bananas.
I was reading up on python operator overloading, and it seems like I could define __or__
and __and__
to get the behavior I want. I think I could do something like this:
basket.fruits = (Apple() & Pomegranate()) | (Banana()) | (Orange() & Grapefruit())
This works just fine making two classes (Or
and And
). When __or__
or __and__
get called, I just have return a new Or
or And
object:
def __or__(self, other):
return Or(self, other)
def __and__(self, other):
return And(self, other)
What I'm trying to figure out is how do I do this without having to instantiate the fruits first? Why can't I use a static __or__
method on the base Fruit
class? I've tried this but it doesn't work:
class Fruit(object):
@classmethod
def __or__(self, other):
return Or(self, other)
and assigning the fruit:
basket.fruits = (Apple & Pomegranate) | (Orange & Grapefruit) | (Banana)
I get an error like this:
TypeError: unsupported operand type(s) for |: 'type' and 'type'
Any thoughts on how to make this work?
__or__
is looked up on the type of the object; for a Fruit
instance, that'll be Fruit
; for Fruit
, that is type
. You can change the type of Fruit
, though, by using a metaclass:
class FruitMeta(type):
def __or__(self, other):
return Or(self, other)
class Fruit(object):
__metaclass__ = FruitMeta
(For Python 3, the syntax is class Fruit(metaclass=FruitMeta):
instead.)
This then does all that you want. Apple | Banana
(assuming these two to be subclasses of Fruit
) will produce Or(Apple, Banana)
.
Be very careful with this sort of design, though. It's tending into the realm of magic and may easily cause confusion.
(Complete demonstration, in 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'>)
这篇关于在 python 类上覆盖 __or__ 运算符的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!