2

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__():
    ...
6
  • 3
    There is no "default", only what is (or might be) inherited from a parent. object.__add__ isn't defined, so effectively nearly every standard class that supports addition defines it from scratch. Commented Aug 18, 2020 at 13:56
  • 2
    change for your own classes: yeah just write the __add__ method to do something different. Change behaviour for built-in types: No, at least not without recompiling python after changing the C source code. Commented Aug 18, 2020 at 13:59
  • 1
    may I ask: Why? Why do you want addition to do subtraction? Do you have any realistic goal or is this a purely theoretical question? There are so many overloadable methods surely if you have a custom behaviour you can use something like x << y Commented Aug 18, 2020 at 14:06
  • 1
    Sure, this is purely theoretical, I am trying to grasp the foundation of built-in types and their limitations and would like to read how the addition / subtraction / etc is defined in the very base level of Python, I assume when its compiled. Commented Aug 18, 2020 at 14:10
  • 2
    @JonasPalačionis If you looking for the very base level, you may want to check out the source for CPython. You can, for instance, find the implementation of int addition here, that of list addition here, tuple addition here, etc. Commented Aug 18, 2020 at 14:28

2 Answers 2

3

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.

Sign up to request clarification or add additional context in comments.

Comments

3

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)

5 Comments

Thank you, I have a question, what do you mean that my __add__ implementation modifies the original instance? I understand that only objects that are made out of class Vec will have the __add__ method modified, but everything else will work as before when using __add__ method. I understand that original instance is class Vec, right?
@JonasPalačionis the __add__ changes self.x and self.y, check v1 after doing the addition with your current implementation.
This works for v1 + v2, but gives an error for more than two instances, like v1 + v2 + v3 + ... + vn. What if I wanted to add as many instances as I wanted?
@Jonas I have tested it with 3 instances and it worked OK. What is the actual code? What error? What python version? ... maybe a topic for a new question
The problem original instance getting modified can be avoided by simply returning return Vec(self.x + other.x, self.y + other.y)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.