It’s actually quite fun that after years of using something, you still find a new way to do something. So at the last Sydney Python meet up, there were showings of how Python interfaces objects.
Consider this for example:
class Blah(object):
''' skipping the __init__ and stuff '''
def __add__(self, other):
# skips checks and stuff
return self.value + other
>>> b = Blah(2)
>>> b + 2
4
However, it was pointed out by my friend Julian, that the other way wouldn’t work – that operator overloading was only left associative:
>>> b = Blah(2)
>>> 2 + b
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'Blah'
Last night as I was preparing my slides and code for my PyConAU talk, I accidentally found this. More specifically, I found out about the __radd__
, __rmul__
etc methods.
So, if you implement both __add__
and __radd__
interface methods, you can have right associativity:
class Blah(object):
''' skipping the __init__ and others '''
def __add__(self, other):
# skips checks and stuff
return self.value + other
def __radd__(self, other):
return self.__add__(other)
>>> b = Blah(2)
>>> b + 2
4
>>> 2 + b
4
.
Here’s Julian’s proof of concept to show that ambiguities don’t matter:
class Multiplier(object):
def __init__(self, description):
self.description = description
def __mul__(self, b):
print ("__mul__ was called on {0}".format(self.description))
def __rmul__(self, b):
print ("__rmul__ was called on {0}".format(self.description))
def __int__(self):
return 43
a = Multiplier("a")
b = Multiplier("b")
# Confirm Chew's finding still works.
a*5
5*a
# Which gets priority in this ambiguous situation? Turns out __mul__ does.
a*b
# But, we can force it.
int(a)*b
So, there you go… kinda cool, eh?