sábado, 9 de junho de 2012

Decorator de decorators

E ai pessoal, tudo bem?

Bom, o post de hoje vai ser bem curto, pretendo apenas aproveitar uma oportunidade e abordar um assunto.

A alguns dias, um amigo do trabalho me perguntou se seria possível criar um decorator de decorators. O objetivo era simples, evitar a repetição de chamadas a decorators muito usados e essa a sintaxe feia abaixo:

@decorator_a
@decorator_b
@decorator_c
def teste(bla):
    print bla
Se você tem dúvidas sobre o que é um decorator, veja a descrição na Wikipedia aqui.

Depois de pensar um pouco, disse que seria possível com a forma mais verbosa de se usar um decorator. Vejam o exemplo abaixo:

def decorator_a(func):
    def wrapper(*args, **kwargs):
        print 'a'
    return func(*args, **kwargs)
return wrapper

def decorator_b(func):
    def wrapper(*args, **kwargs):
        print 'b'
    return func(*args, **kwargs)
return wrapper

def decorator_c(func):
    def wrapper(*args, **kwargs):
        print 'c'
    return func(*args, **kwargs)
return wrapper

def decorator_all(func):
    def wrapper(*args, **kwargs):
        res_c = decorator_c(func)
        res_b = decorator_b(res_c)
        res_a = decorator_a(res_b)
    return res_a(*args, **kwargs)
return wrapper

@decorator_all
def teste(bla):
    print bla

if __name__ == '__main__':
    teste('bla')

É importante reparar que a ordem das chamadas das funções, da linha 21 a 24, deve ser inversa a ordem usada na declaração com arroba (@). Isso se da pela forma que as funções são chamadas nas diferentes sintaxes e, em alguns casos, isso pode fazer diferença.

Veja a saída do código acima:
rafael@local ~$ python decorators.py
a
b
c
bla
rafael@local ~$

Espero que tenham gostado. Um abraço e até o próximo post!

2 comentários:

  1. Muito show.
    Caso interesse, mais chique ainda seria seria um decorator com parâmetros.
    Sendo o segundo parâmetro do decorator_all uma lista, pode-se interar através dela e usar o decorator_all de forma genérica.
    Não testei, mas acho que não haveria problemas.
    Uma aplicação comum de decorator com parâmetros é uma das formas de configurar views no framework pyramid:

    @view_config(context=Root, renderer='index.mako')
    def index(context, request):
    return {'index': context}

    ResponderExcluir
  2. Obrigado Savio!

    Acredito que seja possível sim, passando uma lista de funções decoradoras, ótima abordagem!

    ResponderExcluir