andrews medina

Monkey Patch

escrito em 21/03/2008

Motivado pelos posts sobre paradigmas de programação escritos pelo Ronaldo Ferraz e a uma discussão sobre como alterar uma classe em tempo de execução no Python, resolvi escrever sobre esse assunto aqui no blog.

Moneky Patch é como é chamada a possibilidade de poder alterar uma classe em tempo de execução. Existem linguagens em que o Monkey Patch é muito utilizado, como o Ruby, mas é possível utilizar essa técnica em Python também.

Em Ruby para alterar uma classe basta apenas chamar a classe novamente e assim extendê-la:

Vamos criar uma classe chamada Teste:

class Teste
    def oi
        puts 'oi'
    end
end

Vamos alterar ela. Como citei acima basta apenas recriar ela com o novo método:

class Teste
    def ola
        puts 'ola'
    end
end

Em Ruby é permitido alterar classes nativas e métodos já existentes. Dessa forma se eu quiser adicionar um método a classe String eu posso.

class String
    def conta
        return self.size()
    end
end

Em Python as coisas funcionam um pouco diferente. Vamos criar uma classe sem métodos chamada Teste:

>>>class Teste: pass
>>> dir(Teste)
['__doc__', '__module__']

Vamos criar uma função para adicionar futuramente como método a classe Teste

>>> def oi(self): print 'oi'

Para adicionar essa função como método da classe basta criar um atributo na classe e atribuir a função a esse atributo da seguinte maneira:

>>> Teste.oi = oi

E para confirmar que está tudo funcionando como esperado, vamos criar uma instância para a classe Teste e executar o método oi:

>>> teste = Teste()
>>> teste.oi()
oi

Outra forma de adicionarmos um método a uma classe é usando o método setattr:

>>>setattr(Teste, oi.__name__, oi)

Agora com esse conhecimento é possível, automatizar isso criando decorators ou metaclasses como num exemplo dado pelo Guido van Rossum.

Só uma observação. Diferente de Ruby, no Python não é possivel fazer monkey patch em classes built-in como str, int, list.

Vamos a um exemplo:

>>> def conta(self): print len(self)

>>> setattr(str, conta.__name__, conta)

Traceback (most recent call last):
  File "", line 1, in 
TypeError: can't set attributes of built-in/extension type 'str'

Para finalizar, ter o recurso do monkey patch numa linguagem é algo muito poderoso, e alguém já disse que 'com grandes poderes, vem grandes responsabilidades'. Se você alterar uma classe usando o monkey patch com um método que já existe nessa classe, isso pode ser algo bem traiçoeiro.


blog comments powered by Disqus