There is a difference between the methods (without dunders) and the dunders methods.
Example:Operator.setitem(a, b, c) and operator.__setitem__(a, b, c)?
There’s probably no difference - (as in the other answer, they’re just aliases).
The recommendation however is: let the language call internally the names with "Dunder", and, when making explicit calls, always use the name without Dunder - when there is.
As we have seen some of the Operator module methods make calls to internal object methods if it has been defined. But
I honestly don’t know why, I’m led to believe that when we do
an operation for example of sum, is somehow invoked
Operator.add(or operator.__add__), am I wrong?
in fact, it is the opposite - in Python, each class defines how objects will behave with operators - that is, the language allows "Operator Overriding". The way this is done by language is described in the document called Data Model in language documentation. . In short - the Dunder methods in class is that contains the code that will be executed when instances of that class are involved with the use of operators, or other actions involving the "Dunder methods".
It is easy to see that the flow is this when trying to imagine the opposite: if the "specific code" for the add of each class was in the "power" of operator.add, and not in every class, where developers would put the code to the operator operator.add use? Or even thinking about the existing code - would it make sense to operator.add centralize both the code for the addition of sequences (which is concatenation) and numbers (addition)?
So the path is the opposite - the module operator is a "nice to have", but by no means essential in any Python program.
In practice, it is just a way of maintaining the rule - more of style than necessity - of "you don’t need to call the 'Dunder' methods directly". So you can write operator.neg(b) instead of b.__neg__(). (For binary operators, the functions in Operator do a little more - why they also implement the logic of calling the reverse sum - __radd__, in the second object of an operation, if the sum between the object types of the expression is not implemented in the first object).
So much so that it contains the mathematical operators and others with proper syntax in the language - whose most common use is in expressions that get fixed in the program (that is - it is more common for you to write a = b + c than a = add(b, c)). However, some Dunder methods that do not have special syntax have the call equivalent to those in the module operator direct as built-in language - for example, the functions len and hash which call respectively the methods __len__ and __hash__
One of the uses that module operator has is when, at the time you write a type of code, you don’t know yet what operation will be executed between two operands - for example, a calculator program can check whether the user has typed "-" or "+" to choose "Operator.add" or "Operator.sub" programmatically, more elegantly than a sequence of ifs where the expression is repeated every time:
Instead of:
if operacao == "+":
a = b + c
elif operacao == "-":
a = b - c
...
it is possible to write something like:
operacoes = {'+': operator.add, '-': operator.sub, ...}
a = operacores[operacao](b, c)
and that being said, some members of the "Operator" module still do something else - for example, the itemgetter, attrgetter and methodcaller return a reusable calling object (which can be called as if it were a function), which can be used with several different objects in creating rather elegant code.
For those interested, module source code
operator: https://github.com/python/cpython/blob/master/Lib/operator.py#L420– Woss