Rafael Rocha


List comprehensions

Enviado em Python por rsrocha no Agosto 19, 2008
Tags: , , , ,

Uma das principais razões de eu ter gostado do Python logo de cara foi a maneira como ele lida com as listas. Na época eu estava começando a estudar sobre linguagens fucionais, e uma das coisas que mais me atraía era a expressividade que estas linguagens ofereciam. Python, que também é uma linguagem funcional (além de orientada a objeto e procedural), acaba se beneficiando desta característica.

O assunto é extenso, por isto agora vou focar só em compreesões de listas (list comprehensions). Se você não faz a menor idéia do que isto significa nem pra quê pode servir, pense no seguinte: é uma maneira muito simples e elegante de definir uma sublista dentro de uma lista. Lembra da teoria dos conjuntos? Falar em matemática pode complicar as coisas, então vamos para um exemplo prático. Imagine que você recebe uma frase e precisa verificar se ela contém alguma palavra de baixo calão. As palavras de baixo calão estão definidas em uma lista. Pra deixar as coisas mais divertidas, vou usar alguns palavrões de verdade:

frase = 'Mandei a atendente eletrônica pra puta que pariu'
proibidas = ['puta','vb']

Qual a melhor maneira de verificar se a frase contém algum palavrão e ainda de quebra listar quais palavrões foram ditos? Em Python, usando alguns conceitos de programação funcional, a coisa fica bem simples:

resultado = [ palavra for palavra in frase.split() if palavra in proibidas ]

Peraí! Se você nunca teve contato linguagens funcionais, esta linha pode parecer meio estranha. Vá com calma e leia ela de novo. Como eu falei no início do artigo, o objetivo disto tudo é expressividade, ou seja, dizer muita coisa com poucas palavras. A linha acima retorna para a variável “resultado” uma lista com todas as palavras que estavam na frase e que também estavam na lista de palavras proibidas. Para imprimir o resultado e comprovar:

if resultado:
  print 'Segura a onda! Voce falou os seguintes palavroes: %s' % ', '.join(resultado)
else:
  print 'apravado pelo terrível censor digital'

Se você não conhece o método join, é o seguinte: ele recebe uma lista como parametro e retorna todos os valores da lista separados pela representação do objeto que chamou o método. Soa complicado, mas é bem simples:

lista=['uma','lista','simples']
print ', '.join(lista)

Isto vai resultar na seguinte saída:

uma, lista, simples

Processamento de strings é outro ponto forte do Python. Este só valeu a pena ser mencioado neste artigo porque envolvia listas; agora vamos voltar pro nosso foco, list comprehensions. No final das contas, nosso exemplo ficou assim:

frase = 'Mandei a atendente eletrônica pra puta que pariu'
proibidas = ['puta','vb']
resultado = [ palavra for palavra in frase.split() if palavra in proibidas ]
if resultado:
  print 'Segura a onda! Voce falou os seguintes palavroes: %s' % ', '.join(resultado)
else:
  print 'apravado pelo terrível censor digital'

Se você executar este código, vai receber a seguinte a saída:

Segura a onda! Voce falou os seguintes palavroes: puta

E se quiser dar uma enfeitada e mudar a sua frase:

frase = 'Mandei a atendente eletrônica escrita em vb pra puta que pariu'

Vai ter a seguinte saída:

Segura a onda! Voce falou os seguintes palavroes: vb, puta

Saber trabalhar bem com listas é um trunfo que todo desenvolvedor tem que ter na manga. Existe muito mais além do que foi citado neste artigo; vale a pena estudar o assunto a fundo, seu código só tem a melhorar.

Chamada dinâmica de métodos no Python

Enviado em Python por rsrocha no Agosto 18, 2008
Tags: , , , ,

Chamada dinâmica de métodos é um dos conceitos chave para construção de frameworks, e também uma peça fundamental para implementação de design patterns como inversão de controle. A idéia é a seguinte: permitir que um método seja chamado durante a execução do programa, porém sem que você saiba em tempo de codificação que método será este.

Uma boa prática de programação é tentar parametrizar tudo, sem deixar nada “forçado” no código. Usando algumas ferramentas de reflexão e introspecção disponiveis no Python podemos levar isto além, e começar a parametrizar até mesmo a chamada de métodos, algo que pode aumentar muito a facilidade de manutenção do seu programa.

Vamos usar como exemplo uma classe chamada UserController. A classe tem três métodos, e se parece com isto:

class UserController:

  def handle(self,action):
    pass

  def login(self):
    print 'Executou o método login()'

  def edit(self):
    print 'Executou o método edit()'

A interface para uso da classe é o método handle; ele recebe o nome de uma ação e então se responsabiliza por chamar o método correspondente. Uma solução seria construir um bloco de ifs:

def handle(self,action):
  if action=='login':
    self.login()
  elif action=='edit':
    self.edit()

Só com os dois métodos existentes já dá pra perceber o quanto esta solução é pouco prática e trabalhosa Imagine se você tem 100 métodos diferentes que podem ser chamados, vai escrever 100 desvios condicionais? Pior, e se você quiser estender esta classe? Vai acabar tendo que redefinir o método handle em todas as subclasses. Se o código já estava cheirando mal, agora é que está fedendo de verdade.

Uma solução simples é utilizar o poder de reflexão e introspecção embutidos no Python. A função hasattr verficia se um determinado método existe ou não em um objeto. Vamos alterar nosso método para verificar dinamicamente se a ação passada como parâmetro existe no objeto:

def handle(self,action):
  if hasattr(self,action):
    pint 'Metodo existente'
  else:
    print 'Metodo inexistente'

Agora vamos além: ao invés de simplesmente verificar se o método existe, vamos também executá-lo. O nome da função é getattr, e as semelhanças com hasattr não param por aí; os parâmetros são os mesmos, diferença é o retorno. No caso de hastattr, um valor booleano indicando se o atributo (métodos também são atributos) existe; no caso de getattr, o valor retornado é o próprio método. Estranho? O código abaixo pode deixar as coisas um pouco mais claras:

def handle(self,action):
  if hasattr(self,action):
    method = getattr(self,action)
    method()
  else:
    print 'Metodo inexistente'

Muito simples! A variável “method” agora contém o próprio método referenciado na variável “action”. Agora que você já entendeu o conceito, podemos deixar o código um pouco mais bonito:

def handle(self,action):
  if hasattr(self,action):
    getattr(self,action)()
  else:
    print 'Método inexistente'

O resultado é o mesmo. No final, nossa classe se parece com isto:

class UserController:
  
  def handle(self,action):
    if hasattr(self,action):
      getattr(self,action)()
    else:
     print 'Metodo inexistente'

  def login(self):
    print 'Executou o método login()'

  def edit(self):
    print 'Executou o método edit()'

Para testar:

controller = UserController()
controller.handle('login')

Vale lembrar que executar qualquer coisa dinamicamente pode ser perigoso, e você deve tomar sempre as precauções cabíveis ao seu programa!