The operator module invokes special methods of objects

4

The biblioteca padrão do Python has the module operator , studying the same I noticed some of its functions have say "aliases" , for example:

operator.setitem(a, b, c)
operator.__setitem__(a, b, c)
Set the value of a at index b to c.

To better understand the module I created my own object to check the behavior of the same with the calls of the module.

class Spam:

   def __add__(self, other):
       print('call __add__')


>>> operator.add(Spam(), 3)
"call __add__"
>>> operator.__add__(Spam(), 3) 
"call __add__"

How can operator.add and operator.__add__ call the same special method. My doubts are:

  • There is a difference between métodos(sem dunders) and métodos dunders . Example: operator.setitem(a, b, c) e operator.__setitem__(a, b, c) ?
  • As we have seen, some of the métodos do módulo operator make calls to métodos internos do objeto if it has been defined. But I honestly do not know why, I'm led to believe that when we do an operation for example of sum, it is somehow invoked operator.add (or operator.__add__ ), am I wrong?
asked by anonymous 29.10.2018 / 19:12

3 answers

1
  

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.

    
29.10.2018 / 20:58
0
They are the same thing. Look at the library code from line 248: link

# All of these "__func__ = func" assignments have to happen after importing
# from _operator to make sure they're set to the right function
...
__abs__ = abs
__add__ = add
__and__ = and_

You can do the same thing:

class Spam:
    def __add__(self, other):
        print('call __add__')
    add = __add__

s = Spam()
print(Spam.__add__ == Spam.add)
print(s.__add__ == s.add)
    
29.10.2018 / 19:49
0

I'll use the explanation of Ramalho's Fluent Python book:

The first thing to know about special methods is that they were created to be called by the Python interpreter, not by you. We do not write my_object.__len__() . We write len(my_object) , and if my_object is an instance of a class defined by you, Python will call the __len__ instance method that you have implemented.

However, for built-in types such as list, str, bytearray and others, the interpreter will use a shortcut: the len() implementation of CPython actually returns the value of the ob_size field of struct C PyVarobject that represents qq inline object of variable size in memory. This is much faster than calling a method.

Most often the call to the special method will be implicit. For example, the for i in x: statement causes the call to iter(x) , which in turn can call x.__iter__() if it is available.

If you are not using enough metaprogramming, your code should not have calls to the so-called special methods.

The only special method called directly by the user is often the __init__ to invoke the superclass initializer when the user implements the __init__ itself.

Last but not least, avoid creating attributes with the __foo__ syntax, because in the future these names may acquire special meanings even if they are not currently in use.

  

Edited

About the __init__ call in Luciano's book:

    
29.10.2018 / 19:50