It was probably explained that the +
operator will actually call __add__
under the hood. So when you do S + 'NI!'
then what happens under the hood is that __add__
is actually called (if S
has one). So semantically, both versions do exactly the same thing.
The difference is in what the code corresponds to though. As you probably know, Python is compiled into bytecode which is then executed. The bytecode operations are what determine what steps the interpreter has to execute. You can take a look at the bytecode with the dis
module:
>>> import dis
>>> dis.dis("S+'NI!'")
1 0 LOAD_NAME 0 (S)
2 LOAD_CONST 0 ('NI!')
4 BINARY_ADD
6 RETURN_VALUE
>>> dis.dis("S.__add__('NI!')")
1 0 LOAD_NAME 0 (S)
2 LOAD_METHOD 1 (__add__)
4 LOAD_CONST 0 ('NI!')
6 CALL_METHOD 1
As you can see, the difference here is basically that the +
operator just does a BINARY_ADD
while the __add__
call loads the actual method and executes it.
When the interpreter sees the BINARY_ADD
it will automatically look up the __add__
implementation and call that, but it can do so more efficiently than when you have to look up the method within Python bytecode.
So basically, by calling __add__
explicitly, you are preventing the interpreter from going the faster route to the implementation.
That being said, the difference is negligible. If you time the difference between the two calls, you can see the difference but it is really not that much (this is 10M calls):
>>> timeit("S+'NI!'", setup='S = "spam"', number=10**7)
0.45791053899995404
>>> timeit("S.__add__('NI!')", setup='S = "spam"', number=10**7)
1.0082074819999889
Note that these results don’t always have to look like this. When timing a custom type (with a very simple __add__
implementation), the call to __add__
could turn out to be faster:
>>> timeit("S+'NI!'", setup='from __main__ import SType;S = SType()', number=10**7)
0.7971681049998551
>>> timeit("S.__add__('NI!')", setup='from __main__ import SType;S = SType()', number=10**7)
0.6606798959999196
The difference here is even smaller but +
is slower.
The bottom line is that you shouldn’t worry about these differences. Choose what is more readable, and almost all of the time that will be +
. If you need to worry about performance, then make sure to analyze your application as a whole, and don’t trust such micro-benchmarks. They aren’t helpful when looking at your application, and in 99.99%, the difference between these two ways will not make a difference. It’s much more likely that there is another bottleneck in your application that will slow it down more.