E na mesma linha dos closures, eu passei a saber realmente o que são decorators depois de utilizar o framework Django. Se você está trabalhando com Orientação a Objetos e até agora não se deparou com este recurso, te garanto que um dia você precisará dele… afinal, é um dos Design Patterns mais bacanas (e úteis) que já vi.
Vamos lá “decorar” nossos métodos Python!
Eu não saberia explicar de uma forma melhor do que foi explicado pela Wikipedia, o que é o padrão decorator:
Encontramos na Wiki do Python uma explicação mais objetiva e esclarecedora:
Decorators alteram dinamicamente a funcionalidade de uma função, método ou classe, sem uso direto de subclasses ou alterando o código-fonte da função “decorada”.
O Python começou a dar suporte a decorators a partir da versão 2.4.
Você terá a sua disposição alguns decoradores built-in e também poderá
criar os seus próprios sem muito dificuldade. É possível identificar um
decorator através do caractere @
, por exemplo, a instrução abaixo
declara o método say_hello
da classe People
como estático:
class People:
@staticmethod
def say_hello():
print 'Hello!'
Vale notar que podemos reproduzir o comportamento acima sem utilizar a sintaxe especial de decorators (mas não deixamos de utilizar o conceito):
class People:
def say_hello():
print 'Hello!'
say_hello = staticmethod(say_hello)
Quer conhecer mais sobre decorators em Python? Leia a PEP 318 – Decorators for functions and methods.
Vamos por a mão na massa e criar o nosso próprio decorator:
# meu_decorator.py
def meu_decorador(alvo):
def wrapper():
print 'Chamando a funcao "%s"' % alvo.__name__
return alvo()
return wrapper
@meu_decorador
def meu_alvo():
print 'Eu sou um alvo!'
meu_alvo()
Chamando o script acima, teremos o seguinte resultado:
$ python meu_decorator.py
Chamando a funcao "meu_alvo"
Eu sou um alvo!
Vou tentar seguir um fluxo que deixe claro o que o procedimento está realizando.
O comportamento da função meu_alvo
é muito simples: imprimir “Eu sou um alvo!”
na tela. Mas o @meu_decorador
está lá para
complicar a nossa vida :D
Com a chamada de @meu_decorador
logo acima de meu_alvo
, fica
claro que na verdade estamos passando meu_alvo
como um parâmetro
(alvo
) para o método meu_decorador
, encontrado logo no início
do arquivo. Note que o método retorna wrapper
sem os parênteses no
final (que caracterizam uma chamada de função), ele está retornando
apenas a referência ao método wrapper
, que será de fato “executado”
externamente.
Dentro da função wrapper
temos a impressão da string ‘Chamando a funcao “meu_alvo”‘
e a execução de meu_alvo
. Isto deve-se ao
fato de que alvo
nada mais é que uma referência a função
meu_alvo
, que passamos como argumento para meu_decorador
através do @meu_decorador
logo acima da função meu_alvo
,
certo?
Então resumindo isso tudo, o resultado final é que meu_alvo()
no
final do arquivo na verdade é a execução da referência a wrapper
, ou
seja, é o mesmo que ler “wrapper()“
. Ele fará o print e
posteriormente retornará o resultado de meu_alvo
, que nada mais é
que a impressão de “Eu sou um alvo!”
.
Bacana não? Aqui vai mais um para deixar as coisas um pouco mais claras… vamos simular o esquema de roteamento de um framework web:
rotas = []
def rota(endereco):
def wrapper(fn):
rotas.append((endereco, fn))
return wrapper
@rota('/index/')
def home_view():
return 'Pagina inicial'
@rota('/contato/')
def contato_view():
return 'Pagina de contato'
print rotas[0][1]() # Pagina inicial
print rotas[1][1]() # Pagina de contato
print rotas
# [('/index/', < function home_view at 0xb736580c >),
# ('/contato/', < function contato_view at 0xb7365b8c >)]
Até a próxima…