The article is showing function overriding, not overloading, which is just standard class inheritance stuff.
If python had you write e1.__add__(e2) Or S1.__join__(s2)
Then you would have 2 distinct operators, therefore no overloading.
Re classes.
Foo.len() and bar.len() are different. You can tell that when you are using them.
It's no different to I_add() and f_add() they are both different functions.
If you automatically called one of those depending on whether the arguments were floats or ints, that would be overloading.
I suppose you could take it further and say that it's overloading if you have different int sizes.
But then we don't really class that as overloading, which I suppose demonstrates that it isn't a very precise term so there isn't much point getting particularly pedantic about it.
But there is no limit to where the underscores go: the name _? Would give you x ? postfix. To define if-statements one defines the name if_then_else_.
As to overloading as in multiple signatures (No types here!) for the same method name, it is not possible in the regular sense in Python since an object is more or less a dictionary of names. However, the dynamicity of the language will happily allow you to simulate it with YOUR own rules for disambiguation if you like. In practice, you'd 1. Don't 2. *args, **kwargs, Branches of is None and isinstance, etc.
"self" aged well.
It's not "operator overloading" any more than two _different_ classes having a `length` property is "length overloading".
Using the term "operator overloading" to begin with is the error here.