Operator Overloading With Right Associativity In Python

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?

comments powered by Disqus