Что такое monkey patching?
Я пытаюсь понять, что такое "monkey patching" или "монки патч"?
Это что-то вроде перегрузки методов/операторов или делегирования?
Есть ли у этого что-то общее с этими концепциями?
5 ответ(ов)
Что такое monkey patch?
Проще говоря, monkey patching — это внесение изменений в модуль или класс во время выполнения программы.
Пример использования
Вот пример monkey patching из документации к Pandas:
import pandas as pd
def just_foo_cols(self):
"""Получить список названий столбцов, содержащих строку 'foo'"""
return [x for x in self.columns if 'foo' in x]
pd.DataFrame.just_foo_cols = just_foo_cols # monkey-patch класса DataFrame
df = pd.DataFrame([list(range(4))], columns=["A", "foo", "foozball", "bar"])
df.just_foo_cols()
del pd.DataFrame.just_foo_cols # вы также можете удалить новый метод
Для начала импортируем наш модуль:
import pandas as pd
Затем создаем определение метода, который существует вне каких-либо определений классов (поскольку различие между функцией и не привязанным методом в Python 3 имеет небольшое значение, концепция не привязанных методов была исключена):
def just_foo_cols(self):
"""Получить список названий столбцов, содержащих строку 'foo'"""
return [x for x in self.columns if 'foo' in x]
Теперь просто прикрепим этот метод к классу, который мы хотим использовать:
pd.DataFrame.just_foo_cols = just_foo_cols # monkey-patch класса DataFrame
Теперь мы можем использовать метод на экземпляре класса, а затем удалить его, когда он нам больше не нужен:
df = pd.DataFrame([list(range(4))], columns=["A", "foo", "foozball", "bar"])
df.just_foo_cols()
del pd.DataFrame.just_foo_cols # вы также можете удалить новый метод
Предостережение по поводу изменения имен
Если вы используете изменение имен (предваряя атрибуты двойным подчеркиванием, что меняет имя, и что я не рекомендую), вам придется вручную изменять имя, если вы решите это делать. Поскольку я не рекомендую изменение имен, я не буду приводить здесь примеры.
Пример для тестирования
Как можно использовать это знание, например, в тестировании?
Предположим, нам нужно смоделировать вызов для получения данных из внешнего источника, который приводит к ошибке, чтобы убедиться в правильном поведении в таком случае. Мы можем monkey patch структуры данных, чтобы гарантировать это поведение. (Используя аналогичное название метода, как предложил Дэниел Розман:)
import datasource
def get_data(self):
'''monkey patch datasource.Structure, чтобы смоделировать ошибку'''
raise datasource.DataRetrievalError
datasource.Structure.get_data = get_data
Когда мы протестируем это на поведение, зависящее от повышения ошибки этим методом, при правильной реализации мы получим данное поведение в результатах тестирования.
Просто сделать вышеупомянутое изменит объект Structure
на время выполнения процесса, поэтому вам следует использовать настройки и очистки в ваших модульных тестах, чтобы избежать этого, например:
def setUp(self):
# сохранить ссылку на настоящую реальную функцию:
self.real_get_data = datasource.Structure.get_data
# monkey patch:
datasource.Structure.get_data = get_data
def tearDown(self):
# вернуть реальный метод объекту Structure:
datasource.Structure.get_data = self.real_get_data
(Хотя вышеуказанное решение вполне корректно, вероятно, будет лучше использовать библиотеку mock
для патчинга кода. Декоратор patch
из mock
будет менее подвержен ошибкам, чем приведенный выше способ, который требует больше строк кода и, следовательно, больше возможностей для возникновения ошибок. Я еще не изучал код в mock
, но предполагаю, что он использует monkey-patching аналогичным образом.)
Первое: изменение поведения (monkey patching) — это, на мой взгляд, плохая практика.
Чаще всего оно используется для замены метода на уровне модуля или класса на пользовательскую реализацию.
Самый распространённый случай — это добавление обходного решения для ошибки в модуле или классе, когда нет возможности заменить оригинальный код. В этом случае вы подменяете "неправильный" код через monkey patching на реализацию внутри вашего собственного модуля/пакета.
Monkey patching можно осуществлять только в динамических языках программирования, и Python является хорошим примером такого языка. Например, изменение метода во время выполнения вместо обновления определения объекта — это один из способов применения monkey patching. Аналогично, добавление атрибутов (будь то методы или переменные) во время выполнения также считается monkey patching. Такой подход часто используется при работе с модулями, для которых у вас нет исходного кода, что затрудняет внесение изменений в определения объектов.
Считается, что monkey patching является плохой практикой, потому что это означает, что определение объекта не полностью или точно описывает то, как он на самом деле себя ведёт.
Monkey patching — это технический прием, который позволяет в течение выполнения программы изменить поведение уже существующих классов или методов. Это можно сделать, открыв класс заново и изменив его функциональность. Однако стоит использовать этот подход с осторожностью и только при действительно острой необходимости, так как он может привести к трудным для отладки ошибкам.
Python, как динамический язык программирования, позволяет изменять классы, так как они являются изменяемыми (mutable). Вы можете повторно открыть классы и модифицировать их или даже полностью заменить. Тем не менее, всегда следует помнить о потенциальных рисках и последствиях такого подхода.
Что такое monkey patching? Monkey patching – это техника, позволяющая динамически изменять поведение фрагмента кода во время выполнения.
Зачем использовать monkey patching? Эта техника позволяет нам модифицировать или расширять поведение библиотек, модулей, классов или методов в рантайме, не внося изменения в исходный код.
Заключение Monkey patching – это интересная техника, и теперь мы узнали, как её использовать в Python. Однако, как мы обсуждали, у неё есть свои недостатки, и применять её следует с осторожностью.
Почему используется string.join(list), а не list.join(string)?
Создание словаря с помощью генератора словарей
Как получить полный путь к директории текущего файла?
UnicodeDecodeError: Кодек 'charmap' не может декодировать байт X в позиции Y: символ отображается как <неопределённый>
Найти все файлы с расширением .txt в директории на Python