A function in Python is an object like any other - when we do def func(): ...
, the name func
is associated with the function body that is defined after the def
command.
A decorator is a function (or another callable object) that takes a function as a parameter and returns a function. This new function that is returned by the decorator is what 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 here:
def func():
# faz coisas
func = deco(func)
This code snippet 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 the call the deco
function. 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 were called, in a global variable contador
can be declared like this:
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 this 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 named funcao_decorada
since it is passed as a parameter to the outside function, contar_acessos
).
But you can sophisticate the thing - you can create a function that saves the return value of the function decorated in a dictionary, for example, there you make a simple cache (memoize). You can simply register the decorated function in a list of "functions that do something", and return it without creating a new decorator function. I believe this is what Flask does - it notes its function decorated as a function that responds to an address in the URL.