There is a difference between methods (without dunders) and dunders methods.
Example: operator.setitem (a, b, c) and operator.__setitem__(a, b, c)
?
It probably has no difference at all (as it is in the other answer, they are just aliases).
The recommendation however is: let the language internally call names with "dunder", and when making explicit calls, always use the name without dunder - when any.
As we have seen, some of the methods in the operator module make calls to internal methods of the object if it has been defined. But
I honestly do not know why, I'm led to believe that when we do
an operation such as sum, is somehow invoked
operator.add (or operator.__add__
), am I wrong?
In fact, the opposite is true - in Python, each class defines how objects will behave with operators - that is, language allows operator overriding. The way this is done by the language is described in the document called Data Model in language documentation . In short - the dunder methods in the class is that it contains the code that will be executed when instances of that class are involved with the use of operators, or other actions involving "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 is in "power" of operator.add
, not in each class, where would the developers put the code for the operator.add
operator to use? Or even thinking about existing code - would it have felt that the code of operator.add
centered both the code for addition of sequences (which is concatenation) and numbers (addition)?
So the path is the other way around - the operator
module is a nice to have, but by no means essential in any Python program.
In practice, it's just a way of keeping just the rule - more style than necessity - of "you do not need to call 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 - because they also implement the logic of calling the reverse sum - __radd__
, on the second object of an operation, if the sum between the expression object types is not implemented on the first object).
So much it contains the mathematical operators and others with own syntax in the language - whose most common use is in expressions that are fixed in the program (that is - it is more common to write a = b + c
than a = add(b, c)
). However, some dunder methods that do not have a special syntax have the equivalent call to those in the operator
module as built-in language - for example, the len
and hash
functions that respectively call the __len__
and __hash__
One of the uses that the operator
module has is when, at the time you write a type of code, you do not yet know which operation will be performed between two operands - for example, a calculator program can check if the user has typed "-" or "+" to choose "operator.add" or "operator.sub" programmatically, more elegantly than a string of if
s where expression is repeated every time:
Instead of:
if operacao == "+":
a = b + c
elif operacao == "-":
a = b - c
...
You can write something like this:
operacoes = {'+': operator.add, '-': operator.sub, ...}
a = operacores[operacao](b, c)
And all said, some members of the "operator" module still do something else - for example, itemgetter
, attrgetter
and methodcaller
return a callable object (which can be called as a function) , reusable, which can be used with several different objects in quite elegant code creation.