← Назад к вопросам
В каких ситуациях применяется monkey patching
1.8 Middle🔥 71 комментариев
#Python Core
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Monkey Patching в Python
Monkey patching — это динамическое изменение поведения класса или модуля во время выполнения программы. Эта техника спорная и часто опасна, но иногда необходима.
Что это
class Calculator:
def add(self, a, b):
return a + b
calc = Calculator()
print(calc.add(2, 3)) # 5
def new_add(self, a, b):
return a + b + 100
Calculator.add = new_add
calc = Calculator()
print(calc.add(2, 3)) # 105
Ситуация 1: Тестирование
import requests
from unittest import mock
class UserService:
def get_user(self, user_id):
response = requests.get(f"https://api.example.com/users/{user_id}")
return response.json()
def test_get_user():
with mock.patch("requests.get") as mock_get:
mock_get.return_value.json.return_value = {"id": 1, "name": "Alice"}
service = UserService()
user = service.get_user(1)
assert user["name"] == "Alice"
Ситуация 2: Исправление bug в библиотеке
import third_party_library
original_func = third_party_library.process_data
def patched_process_data(data):
if isinstance(data, str):
data = data.strip()
return original_func(data)
third_party_library.process_data = patched_process_data
Ситуация 3: Добавление методов к стандартным типам
original_count = str.count
def count_case_insensitive(self, substring):
return original_count(self.lower(), substring.lower())
str.count_insensitive = count_case_insensitive
text = "Hello HELLO hello"
print(text.count_insensitive("hello")) # 3
Ситуация 4: Dependency injection для тестирования
class Database:
def connect(self):
return psycopg2.connect("dbname=prod")
class TestDatabase:
def connect(self):
return sqlite3.connect(":memory:")
Database = TestDatabase
db = Database()
conn = db.connect()
Ситуация 5: Исправление несовместимости версий
import json_lib
original_parse = json_lib.parse
def compatible_parse(data):
try:
return original_parse(data)
except ValueError:
return None
json_lib.parse = compatible_parse
Ситуация 6: Логирование и мониторинг
class PaymentService:
def process_payment(self, amount):
return amount * 1.1
original_process = PaymentService.process_payment
def logged_process_payment(self, amount):
print(f"Processing: {amount}")
result = original_process(self, amount)
print(f"Result: {result}")
return result
PaymentService.process_payment = logged_process_payment
Опасные примеры
class UserRepository:
def get_all(self):
return []
UserRepository.get_all = lambda self: [{"id": 1}]
# Проблемы:
# 1. Непредсказуемое поведение
# 2. Сложно отладить
# 3. Конфликты в разных модулях
# 4. Невозможно отследить изменения
Лучшие практики
# Используем наследование вместо patching
class CalculatorV2(Calculator):
def add(self, a, b):
return super().add(a, b) + 100
# Dependency Injection
class Service:
def __init__(self, calculator=None):
self.calculator = calculator or Calculator()
# Контекстный менеджер — patch только внутри блока
with mock.patch("module.function", new=replacement):
pass
# После блока всё вернулось в норму
Когда можно использовать
- В тестах (pytest fixtures, mock)
- Для срочного исправления критических bagов
- Для совместимости версий (временно)
- Для добавления логирования/мониторинга в боевом коде
Когда НЕЛЬЗЯ
- В production коде без очень веской причины
- Для изменения основной логики
- Без документирования
- В глобальном скоупе где много модулей
Лучше использовать наследование, Dependency Injection или явный patching в тестах. Monkey patching — это инструмент для экстренных ситуаций, не для повседневной разработки.