Monkey Patch | Python
- Monkey Patch | Python
Definição
Monkey patch é a técnica de alterar dinamicamente classes, métodos ou funções em tempo de execução, sem modificar o código-fonte original onde esses elementos foram definidos.
No Python, isso é possível porque:
- Classes são objetos mutáveis
- Funções são objetos de primeira classe
- A resolução de atributos ocorre em runtime
- O binding de métodos é dinâmico
Tecnicamente, trata-se da redefinição ou injeção de atributos em objetos já carregados na memória do interpretador.
Fundamento Técnico
Quando um atributo é acessado em um objeto:
obj.metodo()
O Python realiza:
- Busca no
__dict__da instância - Busca na classe
- Busca nas classes base (MRO – Method Resolution Order)
Monkey patch funciona porque é possível modificar dinamicamente o __dict__ da classe ou do módulo.
Substituindo um Método de Classe
Classe Original
class Usuario:
def saudacao(self):
return "Olá"
Aplicando Monkey Patch
def nova_saudacao(self):
return "Olá, mundo!"
Usuario.saudacao = nova_saudacao
u = Usuario()
print(u.saudacao()) # Olá, mundo!
Aqui ocorre:
- Substituição direta do atributo
saudacaona classe - Todas as instâncias passam a utilizar o novo comportamento
Alterando Função de um Módulo
import math
def fake_sqrt(x):
return 42
math.sqrt = fake_sqrt
print(math.sqrt(9)) # 42
Impacto:
- Afeta todo o escopo onde o módulo foi importado
- Pode alterar comportamento global do processo
⚠️ Isso altera o comportamento esperado da biblioteca padrão.
Adicionando Método Dinamicamente
class Produto:
pass
def calcular_desconto(self):
return 10
Produto.calcular_desconto = calcular_desconto
p = Produto()
print(p.calcular_desconto()) # 10
Nesse caso:
- O método não existia originalmente
- Foi injetado no namespace da classe
Monkey Patch em Instância Específica
Também é possível alterar apenas uma instância:
class Cliente:
def status(self):
return "ativo"
c1 = Cliente()
c2 = Cliente()
def status_bloqueado(self):
return "bloqueado"
c1.status = status_bloqueado.__get__(c1, Cliente)
print(c1.status()) # bloqueado
print(c2.status()) # ativo
Aqui o patch é aplicado apenas a c1.
Uso em Testes Automatizados
Ferramentas como unittest.mock permitem monkey patch controlado.
Exemplo:
from unittest.mock import patch
with patch("math.sqrt", return_value=99):
import math
print(math.sqrt(4)) # 99
Nesse caso:
- O patch é temporário
- É restaurado após o bloco
with - Reduz risco de efeitos colaterais
Casos de Uso
Testes (Mocking)
Substituir dependências externas como:
- APIs
- Banco de dados
- Serviços HTTP
Hotfix Temporário
Corrigir comportamento de biblioteca sem modificar código-fonte.
Instrumentação
Adicionar logging ou métricas em funções de terceiros.
Riscos e Problemas
- Quebra de previsibilidade
- Efeitos colaterais globais
- Conflitos entre bibliotecas
- Debugging complexo
- Violação do princípio da menor surpresa
Em sistemas grandes, monkey patch pode gerar comportamento difícil de rastrear.
Alternativas Mais Seguras
Em arquiteturas robustas, recomenda-se:
- Injeção de dependência
- Decorators
- Wrappers
- Composição
- Herança controlada
- Strategy pattern
Monkey patch deve ser considerado uma ferramenta de baixo nível.