sábado, 19 de novembro de 2011

Decorators em Python: o que a Annotation gostaria de ser quando crescer

 Olá Pessoal,

 Devido à correria, faz muito tempo que não atualizo o blog. Atualmente tenho aprendido bastante sobre a linguagem Python e tenho gostado muito.

 Durante o desenvolvimento do Revelação Virtual,  desenvolvi um framework para facilitar a minha vida na infra do Google App Engine. Logo no início, comecei a pensar como seria um bom modelo de segurança. Já tive contato com Spring Security em minha época de Javeiro.

 Contudo, buscava uma solução sucinta, sem depender de frameworks e suas documentações. Por  não ter  paciência de ler, fico muito ansioso por ver as coisas funcionar. Se houver mais que uma página de documentação para ler antes de ver a um exemplo concreto funcionando, fico frustrado. Justo nessa época um amigo me falou sobre os decorators de Python (não confundir com o padrão Decorator, veja esse artigo sobre o assunto).

Programação Funcional


 Para utilizar os decorators, você precisa conhecer um conceito simples de programação funcional: função é uma variável. Para quem conhece o conceito, isso pode parecer simples, mas para quem não está acostumado, como era meu caso quando só conhecia Java, função ser uma variável pode soar bem estranho.

 Para clarificar:

def funcao(arg):
  print "Sou uma funcao e meu argumento eh: %s"%arg
 
variavel=funcao

variavel("argumento")

print type(variavel)

Na primeira linha é criada uma função em Python. Contudo, o que realmente acontece é a criação de um Objeto do tipo função cujo identificador é "funcao". Tanto isso é verdade que após a implementação do corpo, o objeto função é é atribuído à "variavel".

 Depois dessa atribuição, a função é executada através da referência "variavel". Para finalizar, o programa imprime o tipo de "variavel": function. Esse é apenas um exemplo simples, mas suficiente, para entender as próximas explicações. Se quiser saber mais, leia a documentação do próprio Python.

Segurança


Depois de entender que função é variável, é possível expandir os horizontes e utilizar funções de uma nova maneira. Com esse "novo" conceito, agora você pode passar funções como argumento e retornar funções como resultado de uma função. Suponha então que você queira executar uma função somente se for passado um valor positivo como argumento único, sem saber qual é essa funcao a princípio. O código poderia ser algo como:

def permitir_valor_positivo(regra_negocio):
  def f(valor):
    if valor>0:
      return regra_negocio(valor)
    return "Somente valor positivo permitido"
  f.__name__=regra_negocio.__name__
  return f


Vamos entender o código:
  1. É criada uma função permitir_valor_positivo que ira receber uma outra como argumento
  2. É criada uma função f que executa a função regra_de_negocio, passada como argumento, somente se o valor passado para f como argumento for positivo. Caso contrário, imprime uma mensagem de erro.
  3. O nome da função é alterado de "f" para o nome da função "regra_de_negocio"
  4. f é retornada como resultado  de permitir_valor_positivo

 Construida nossa funcao , podemos agora apenas ter a preocupação de escrever a "regra de negócio", e depois aplicar a "segurança":


def deposito(valor):
  return "Deposito no valor de: %s"%valor
 
deposito=permitir_valor_positivo(deposito)

print deposito(1)
print deposito(0)


 Nossa regra de negócio é realizar um depósito em conta. Depois de implementado o negócio, podemos fazer sua segurança utilizando nossa função permitir_valor_positivo. Depois de aplicada a segurança, tentamos fazer depósitos com valores 1 e 0 recebendo como resposta, respectivamente, "Deposito no valor de: 1" e "Somente valor positivo permitido".

Decorator


Depois de entendido o conceito acima, poderia-se utilizar a programação funcional para fazer nosso lógica de segurança em pseudo-código:

seguranca(negócio):
     se (cumpre requisitos de segurança):
            execute negocio
     se não:
            Exiba mensagem de erro

Com a função de segurança escrita, bastaria utilizar o esquema da seção anterior para "segurar" os métodos de negócio. Contudo, o Python apresenta uma açúcar sintático bem interessante chamado Decorator. Talvez inspirado pela notação de Annotation em de Java, podemos substituir as linhas abaixo:


def deposito(valor):
  return "Deposito no valor de: %s"%valor

deposito=permitir_valor_positivo(deposito)


por apenas:


@permitir_valor_positivo
def deposito(valor):
  return "Deposito no valor de: %s"%valor


Dessa maneira, o código fica menor e muito mais elegante atingindo alguns objetivos que busco em frameworks:

  1. Documentação curta e simples (esse blog apenas)
  2. Sem necessidade de configuração, apenas conhecimento da própria linguagem
  3. Lógica de Segurança separada da Lógica de Negócio
  4. Granular ao nível de função


Conclusões


Para fazer algo análogo em Java, seria necessário criar uma Annotation personalizada, configurar algum tipo de processamento (como o Spring no Bean Processor) e aplicar a API de Reflection. Como trabalhei justamente com Annotation e Reflection no projeto de meu TCC (JColtrane) sei que esse processo seria muito mais trabalhoso e bem menos elegante que a solução Python, além de obrigar o usuário a ter mais uma dependência em seu projeto. Como já ouvi inúmeras vezes: "para quem só conhece martelo, tudo é prego" ;)

 Se você gostou do exemplo e quer saber mais sobre Python, confira o  curso Python Pro.

 Abs e até o próximo post,
 Renzo Nuccitelli

PS:Gostaria de explicar o título do post. Não gostos de flames, cada um tem o direito de preferir trabalhar com a linguagem que quiser. Contudo, um ponto positivo do flame é gerar discussão e consequente tráfego no blog ;)