Can I change the default __add__ method in Python?

Viewed 964

Is it possible to change the default __add__ method to do something else than just add?

For example, if the goal is with this line: 5+5 get The answer is 10 or anything else like 0 by changing __add__ to be x-y instead of x+y?

I know I can change __add__ in my own classes:

class Vec():
    def __init__(self,x,y):
        self.x = x
        self.y = y
    
    def __repr__(self):
        return f'{self.x, self.y}'

    def __add__(self, other):
        self.x += other.x
        self.y += other.y
        return Vec(self.x,self.y)

    
v1 = Vec(1,2)
v2 = Vec(5,3)

v1+v2
# (6, 5)

Can I somehow target the default __add__ method to change its behaviour? I intuitively think that __add__ is defined in each default data type to return specific results, but then again, the __add__ method is what we address when changing it for a specific class, so, is it possible to change the main __add__ logic?

Something along these lines?

class __add__():
    ...
2 Answers

Yes, you can overload whatever in user defined classes.

class Vec():
    def __init__(self,x,y):
        self.x = x
        self.y = y
    
    def __repr__(self):
        return f'{self.x, self.y}'

    def __add__(self, other):
        self.x += other.x
        self.y += other.y
        return Vec(self.x,self.y)

    
v1 = Vec(1,2)
v2 = Vec(5,3)

print(v1+v2)

# using lambda function
Vec.__add__ = lambda self,other: Vec(self.x-other.x,self.y-other.y)
print(v1+v2)

# using "normal" function
def add(self,other):
    self.x -= other.x
    self.y -= other.y
    return Vec(self.x,self.y)
Vec.__add__ = add
print(v1+v2)

Will not work for built-in types, e.g. resulting in TypeError: can't set attributes of built-in/extension type 'set'

Also please note that your implementation of __add__ modifies the original instance, which I don't like.. (just my note)

If you are looking for information about how the C level built-ins are defined you would want to look at some of the source code, note that I'm linking specifically to floats but the structure exists for all number types:

static PyNumberMethods float_as_number = {
    float_add,          /* nb_add */
    float_sub,          /* nb_subtract */
    float_mul,          /* nb_multiply */

this is the structure for all the C function pointers that implement number methods, (for floats in this case) each builtin type that defines any number related methods will define a PyNumberMethods structure, this is then used in the formal definition of the type:

PyTypeObject PyFloat_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "float",
    sizeof(PyFloatObject),
   
    ...
        
    &float_as_number,                           /* tp_as_number */

the PyTypeObject represents all the relevant information needed to construct the float object in python, (or equivalently, int or str etc.) containing all the methods, attributes, and necessary meta-data to work as a python type. So if you really wanted to change adding floats to instead do another well defined task you'd just change it to point to the other function:

static PyNumberMethods float_as_number = {
    float_sub,          /* nb_add.  overrides to do subtraction because I want to break everything >:D */
    float_sub,          /* nb_subtract */

If you wanted to write your own behaviour you could write your own function and point to it in this structure instead.

Related