Post's com a tag "ruby"

21
Mar

Monkey Patch

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.