terça-feira, 28 de abril de 2009

O @ do Phyton parece com o do Java, mas não é!

Como desenvolvedor experiente em Java (e usando anotações a vários anos) mas iniciante em Python, eu andei um pouco intrigado com a sintaxe @algumacoisa encontrada em alguns códigos de aplicações Django. Estudei Python pelo Python Tutorial e pelo livro Dive in Python mas, em momento algum, notei esta sintaxe. Então, procurando saber mais a respeito, descobri que ela foi inserida no Python 2.4. O Dive in Python foi escrito com o Python 2.3 e está desculpado... Mas, o Python Tutorial que li, foi para a versão 2.6 do Python. Eu realmente não me lembro de ter visto uma explicação para esta característica da linguagem neste tutorial. De qualquer forma, além da explicação do What's New in Python 2.4, também gostei das explicações de David Mertz em seu artigo Charming Python: Decorators make magic easy e de Phillip Eby no DDJ. Para se aprofundar, e entender porque eu estou curtindo a bessa o Python, leia estes artigos. Mesmo assim, vou dar uma explicação básica sobre o @algumacoisa:

A explicação para esta sintaxe no Python é a seguinte...

Antes do Python 2.2, não existiam métodos estáticos. Isto significa, que não era possível criar uma classe como a definida no código abaixo e executar "metodo", sem criar uma instância, como fazemos em Java. Ou seja, o código Classe.metodo("argumento") daria erro pois não existia o conceito de método estático.

class Classe:
  def metodo(arg):
    print 'arg =', arg

Entretanto, o código acima está sintaticamente correto e pode ser executado através da construção Classe().metodo(), mesmo no Python 2.2. A questão é que ele não vai estar fazendo o que gostaríamos, pois nosso intuito era poder passar um parâmetro para o método. Bem... O Python, como eu disse, segue uma FORTE convenção de que o primeiro parâmetro se chame "self". Mas isto é apenas uma convenção! Então, o que ele interpreta na execução do código acima? Ele simplesmente aceita que você está querendo imprimir o próprio objeto (através de print), mas não aceita você passar um parâmetro para a função como numa chamada a Classe().metodo("teste"). Ele vai dizer que o método está esperando apenas 1 argumento e 2 foram passados (self e "teste", para clarear sua mente...).

Com a introdução das funções build-in classmethod e staticmethod no Python 2.2, foi possível a modificação do código para o apresentado abaixo e, consequentemente, a execução do código a seguir:

class Classe:
  def metodo(arg):
    print 'arg =', arg

  metodo = staticmethod(metodo)

Classe.metodo("teste1")
Classe().metodo("teste2")

Ou seja, surgiram os nossos conhecidos métodos estáticos! O código semelhante em Java seria:

class Classe {
  static void metodo(String arg) {
    System.out.println("arg = " + arg);
  }

  public static void main(String[] args) {
    Classe.metodo("teste1");
    (new Classe()).metodo("teste2");
  }
}

Quando surgiu, no Python 2.4, o conceito de decorators, passamos a simplificar a linguagem da classe acima, reescrevendo-a da seguinte forma:

class Classe:
  @staticmethod
  def metodo(arg):
    print 'arg =', arg

  Classe.metodo("teste1")
  Classe().metodo("teste2")

Entendeu o que é um decorator em sua essência? Não? Então vamos a mais um exemplo que talvez esclareça mais algumas coisas...

O que o buildin staticmethod faz é pegar um método, e modificar seu comportamento... Isto é parecido com o código abaixo:

def olamundo(metodo):
  def mundo(self):
    return " ".join([metodo(self), 'mundo!'])
  return mundo

class OlaMundo:
  def ola(self):
    return 'Ola'
  ola = olamundo(ola)

print OlaMundo().ola()

A função olamundo recebe um método como parâmetro. Internamente, ela cria um outro método (mundo) que retorna uma string concatenando o resultado da execução do método passado com a string "mundo!". Em seguida, ela retorna esta nova versão do método.

Poderíamos reescrever o código acima, da seguinte forma:

def olamundo(metodo):
  def mundo(self):
    return " ".join([metodo(self), 'mundo!'])
  return mundo

class OlaMundo:
  @olamundo
  def ola(self):
    return 'Ola'

print OlaMundo().ola()

Isto são os decorators do Python... Num outro post, eu falo mais sobre anotações em Java!

Nenhum comentário:

Postar um comentário