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'>)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With