Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can you impose object precedence for overloaded operators in Python?

Say I have two Python classes which both define the add and radd operator overloads, and I add one instance of one class to another instance of another class. The chosen implementation depends on the order in which the items are added (Python looks for an add method on the LHS first, etc).

Is it possible for me to define a precedence on which object's implementation is preferred? I want, for example, that radd is called on the RHS if its precedence is higher than that of the LHS.

I really want to do this for all overloaded operators, so a more general solution is what I'm eventually after.

[edit: added example]

For example, I may have a custom number type class, and I might wish to silently typecast ints to my custom type. Hence I need add and radd (and all the other operator overloads with their 'r' cousins).

Next, I want a generic polynomial class, whose coefficients are some generic number type. I also want to typecast things to polynomials, so I implement add and radd functions for it. However, if I add a custom number on the left with and a polynomial on the right, I want it to be typecast up to a polynomial instead of Python trying to typecast it down to a custom number type. Hence, I want radd to be called instead of add.

like image 389
JeremyKun Avatar asked Feb 03 '26 03:02

JeremyKun


1 Answers

There's no builtin mechanism for defining a "precedence" as you describe. What you could do is implement the precedence checking yourself within the magic methods. That is, you could define the object's __add__ method so that it checks the "precedence" of the other object (however that's defined), and calls that object's __add__ (or __radd__) if its precedence is higher.

Note that, if you just want the LHS __add__ to defer to the RHS __radd__ you can return NotImplemented, which will essentially tell Python "act as if the __add__ method you just called didn't exist".

Here is a sketch of how this could be done with a decorator on the magic methods:

def deco(op):
    def newOp(self, other):
        if other.precedence > self.precedence:
            return NotImplemented
        return op(self, other)
    return newOp

class Thing(object):
    precedence = 0

    def __init__(self, val):
        self.val = val

    @deco
    def __add__(self, other):
        print "Called", self, "__add__"
        return self.__class__(self.val + other.val)

    def __radd__(self, other):
        print "Called", self, "__radd__"
        return self.__class__(self.val + other.val)

class Weak(Thing):
    precedence = 1

class Strong(Thing):
    precedence = 2

This results in the Strong version always being called regardless of the order of operands, so it always returns a Strong:

>>> Weak(1) + Strong(1)
Called <__main__.Strong object at 0x01F96BF0> __radd__
<__main__.Strong object at 0x01F96BD0>
>>> Strong(1) + Weak(1)
Called <__main__.Strong object at 0x01F96B90> __add__
<__main__.Strong object at 0x01F96250>
like image 150
BrenBarn Avatar answered Feb 05 '26 18:02

BrenBarn