Chamada dinâmica de métodos no Python
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!