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
– Wallace Maxters