How do Python decorators work?

Asked

Viewed 2,909 times

33

The @Elizeu Santos asked in the group Python in facebook Portuguese the following:

"Talk guys, I’m studying python and something I don’t understand are the decorators. I faced them making the flask Tuto, can enlighten me?"

(Facebook is not cool to answer questions that include Portuguese and code mixed in the comments - here is much better formatted for this - so I brought the question here, to paste the answer I wrote for him)

1 answer

42


A Python function is an object like any other - when we do def func(): ..., the name func is associated with the body of the function that is defined after the command def.

A Decorator is a function (or other calling object) that takes a function as a parameter and returns a function. This new function is returned by Decorator is associated with the original function name.

That is, suppose a Decorator deco, if used with the syntax of @ does exactly the same as this:

def func():
   # faz coisas
func = deco(func)

That piece of code is the same as:

@deco
def func():
   #faz coisas

In both cases, after this code snippet, the name func is associated with the object that was returned by calling the function deco. In general this object is a function that calls the function func original, but does something before or after calling it.

For example, a decorator who simply records how many times the functions he decorates have been called, in a global variable contador can be declared as such:

contador = 0

def contar_acessos(funcao_decorada):
    # não sabemos quantos parâmetros existirão na chamada
    # da função funcao_decorada, então recebemos *args e **kw
    def nova_func(*args, **kw):
         global contador
         contador += 1
         # e chamamos a função original, com os parâmetros recebidos:
         return funcao_decorada(*args, **kw)
    # retornamos a função "nova_func" - que só faz 
    # incrementar o contador e  retornar o valor da chamada à função original
    return nova_func

# agora vamos usar o decorador:
@contar_acessos
def soma(a, b):
    return a + b

And when we paste that code into the interactive interpreter and call soma sometimes, we have:

>>> contador
0
>>> soma(2,2)
4
>>> soma(3,2)
5
>>> soma("a", "b")
'ab'
>>> contador
3

Then, in this simple case, all that is done is to increase the value of the counter variable before calling the same function that was decorated (and that within the function nova_funcao is under the name of funcao_decorada since it is passed as parameter to the outside function, the contar_acessos).

But you can sophisticate the thing - you can create a function that stores the return value of the decorated function in a dictionary, for example, then you make a simple cache (memoize). One can simply register the decorated function in a list of "functions that do something", and return it itself, without creating a new decorator function. I believe that’s what Flask does - notes its decorated function as a function that responds to an address in the URL.

  • I liked the initiative. As much as I know what are the decorators in Python, I always have to train and reread, not to get confused

Browser other questions tagged

You are not signed in. Login or sign up in order to post.