在 python 类上覆盖 __or__ 运算符 [英] Overriding __or__ operator on python classes

查看:86
本文介绍了在 python 类上覆盖 __or__ 运算符的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

basket = FruitBasket()

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

我正在阅读 python 运算符重载,似乎我可以定义 __or____and__ 来获得我想要的行为.我想我可以做这样的事情:

basket.fruits = (Apple() & Pomegranate()) |(香蕉()) |(橙色()和葡萄柚())

这可以很好地制作两个类(OrAnd).当 __or____and__ 被调用时,我只返回一个新的 OrAnd 对象:

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

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆