[Tutor] could somebody please explain...

Steven D'Aprano steve at pearwood.info
Wed Oct 1 03:38:14 CEST 2014


On Tue, Sep 30, 2014 at 03:54:42PM -0700, Clayton Kirkwood wrote:

> I don't understand the multiplicity of some tools. Namely, why is there a
> 'a+b', operator.add(a,b), operator.__add__(a,b), operator.iadd(a,b),
> operator.__iadd__(a,b) and their related operators?

The + operator is the public interface, but the implementation that 
makes + work are the special methods __add__ and __radd__ .

When you write in your code:

    result = a + b

how does Python know what to do with a and b? In principle, Python could 
hard-code into the language a handful of types that the interpreter 
knows how to add: int, float, str, list, etc. But that is not easily 
extended when a new type is supported, and Python supports "operator 
overloading" where any custom class can define "a + b" to do whatever 
the class developer wants.

So when the Python interpreter executes a + b, when it does is look for 
a special "dunder" (Double UNDERscore) method on a, __add__, or a 
special dunder method __radd__ ("right add") on b, and calls that.

Actually the rules are a bit more complicated than that, which I'm happy 
to explain if you would like, but for simplicity let's ignore __radd__ 
and just say that when Python sees "a + b" what actually gets called is 
a.__add__(b).

So when you create a new class and want it to support the + operator, 
you write a __add__ method:

class Spam:
    def __add__(self, other):
        ...


and now Python knows how to add your Spam instances together.

Sometimes it is useful to treat the + operator as a function, e.g. so 
that you can pass it to another function like reduce. But operators 
aren't values, you can't pass them to functions. This doesn't work:

py> reduce(+, [1, 2, 3, 4])
  File "<stdin>", line 1
    reduce(+, [1, 2, 3, 4])
            ^
SyntaxError: invalid syntax


But you can wrap the operator in a function using lambda:

py> reduce(lambda a, b: a+b, [1, 2, 3, 4])
10


but a more efficient way is to use the pre-made functions in the 
operator module:

py> import operator
py> reduce(operator.add, [1, 2, 3, 4])
10


So for every operator + - * / ** etc. there is a corresponding function 
version in the operator module, add(), sub() etc.


[ Aside: you might not know about reduce(). It takes a function f, and a 
list [a, b, c, d, ...] and calls the function with the first two values:

    result = f(a, b)

then takes that result and repeatedly calls the function again with the 
next value from the list:

    result = f(result, c)
    result = f(result, d)
    ...

until there are no more values left, then returns the final result. 
These days, now that Python has a sum() function, reduce() doesn't get 
used very often. ]

So for each operator that Python knows about, there is the operator 
itself, a function version, and one or two special dunder methods:

  Operator    Function        Dunder methods
  ==========  ==============  =====================
  +           operator.add    __add__  __radd__
  -           operator.sub    __sub__  __rsub__
  *           operator.mul    __mul__  __rmul__
  **          operator.pow    __pow__  __rpow__
  ==          operator.eq     __eq__
  !=          operator.ne     __ne__

etc.

Then there are the special "augmented assignment" operators, so that 
Python can support writing:

  x += 1
  y -= x

etc. Again, the syntax used is a combined operator-assignment += and 
that ends up calling a special dunder method, __iadd__. And again, there 
are special function versions in the operator module.


In summary:

(1) When you want to add two values, use a + b.

(2) When you want a function that adds two values, use operator.add.

(3) When you want to write a class that supports addition, give it 
    the two special dunder methods __add__ and __radd__.

(4) You almost never should call __add__ yourself.



-- 
Steven


More information about the Tutor mailing list