Что такое обезьянье исправление?
Я пытаюсь понять, что такое monkey patching или обезьяний патч?
Это что-то вроде перегрузки методов / операторов или делегирования?
Имеет ли это что-нибудь общее с этими вещами?
Переведено автоматически
Ответ 1
Нет, это не похоже ни на что из вышеперечисленного. Это просто динамическая замена атрибутов во время выполнения.
Для примера рассмотрим класс, у которого есть метод get_data
. Этот метод выполняет внешний поиск (например, в базе данных или веб-API), и его вызывают различные другие методы в классе. Однако в модульном тестировании вы не хотите зависеть от внешнего источника данных - поэтому вы динамически заменяете get_data
метод заглушкой, которая возвращает некоторые фиксированные данные.
Поскольку классы Python изменяемы, а методы являются всего лишь атрибутами класса, вы можете делать это сколько угодно - и, фактически, вы даже можете заменять классы и функции в модуле точно таким же образом.
Но, как указал комментатор, будьте осторожны при monkeypatching:
Если что-то еще, кроме вашей тестовой логики, также вызывает
get_data
, это также вызовет вашу замену, исправленную monkey, а не оригинал - что может быть хорошим или плохим. Просто будьте осторожны.Если существует какая-либо переменная или атрибут, который также указывает на
get_data
функцию, к моменту ее замены этот псевдоним не изменит своего значения и продолжит указывать на оригиналget_data
. (Почему? Python просто повторно привязывает имяget_data
в вашем классе к какому-либо другому объекту-функции; на другие привязки имен это вообще не влияет.)
Ответ 2
MonkeyPatch - это фрагмент кода Python, который расширяет или изменяет другой код во время выполнения (обычно при запуске).
Простой пример выглядит следующим образом:
from SomeOtherProduct.SomeModule import SomeClass
def speak(self):
return "ook ook eee eee eee!"
SomeClass.speak = speak
MonkeyPatch Источник: страница в Zope wiki.
Ответ 3
Что такое обезьянье исправление?
Проще говоря, обезьянье исправление - это внесение изменений в модуль или класс во время выполнения программы.
Пример использования
В документации Pandas есть пример обезьяньего исправления:
import pandas as pd
def just_foo_cols(self):
"""Get a list of column names containing the string 'foo'
"""
return [x for x in self.columns if 'foo' in x]
pd.DataFrame.just_foo_cols = just_foo_cols # monkey-patch the DataFrame class
df = pd.DataFrame([list(range(4))], columns=["A","foo","foozball","bar"])
df.just_foo_cols()
del pd.DataFrame.just_foo_cols # you can also remove the new method
Чтобы разобраться с этим, сначала мы импортируем наш модуль:
import pandas as pd
Далее мы создаем определение метода, которое существует несвязанным и свободным вне области действия любых определений классов (поскольку различие между функцией и несвязанным методом довольно бессмысленно, Python 3 устраняет несвязанный метод):
def just_foo_cols(self):
"""Get a list of column names containing the string 'foo'
"""
return [x for x in self.columns if 'foo' in x]
Далее мы просто прикрепляем этот метод к классу, в котором хотим его использовать:
pd.DataFrame.just_foo_cols = just_foo_cols # monkey-patch the DataFrame class
И тогда мы можем использовать метод в экземпляре класса и удалить метод, когда закончим:
df = pd.DataFrame([list(range(4))], columns=["A","foo","foozball","bar"])
df.just_foo_cols()
del pd.DataFrame.just_foo_cols # you can also remove the new method
Предостережение по поводу искажения имен
Если вы используете искажение имен (добавляя к атрибутам двойное подчеркивание, которое изменяет имя, и которое я не рекомендую), вам придется искажать имена вручную, если вы сделаете это. Поскольку я не рекомендую искажать имена, я не буду демонстрировать это здесь.
Пример тестирования
Как мы можем использовать эти знания, например, в тестировании?
Допустим, нам нужно имитировать вызов запроса данных к внешнему источнику данных, который приводит к ошибке, потому что мы хотим обеспечить правильное поведение в таком случае. Мы можем monkey patching структуру данных, чтобы обеспечить такое поведение. (Итак, используя аналогичное название метода, предложенное Дэниелом Розманом:)
import datasource
def get_data(self):
'''monkey patch datasource.Structure with this to simulate error'''
raise datasource.DataRetrievalError
datasource.Structure.get_data = get_data
И когда мы тестируем его на поведение, основанное на этом методе, вызывающем ошибку, если он правильно реализован, мы получим это поведение в результатах теста.
Простое выполнение вышеописанного изменит Structure
объект на протяжении всего срока службы процесса, поэтому вам захочется использовать настройки и разборки в ваших unittests, чтобы избежать этого, например:
def setUp(self):
# retain a pointer to the actual real method:
self.real_get_data = datasource.Structure.get_data
# monkey patch it:
datasource.Structure.get_data = get_data
def tearDown(self):
# give the real method back to the Structure object:
datasource.Structure.get_data = self.real_get_data
(Хотя вышесказанное прекрасно, вероятно, было бы лучшей идеей использовать mock
библиотеку для исправления кода. mock
patch
декоратор был бы менее подвержен ошибкам, чем выполнение вышеописанного, что потребовало бы большего количества строк кода и, следовательно, больше возможностей для внесения ошибок. Мне еще предстоит просмотреть код в mock
, но я предполагаю, что он использует обезьянье исправление аналогичным образом.)
Ответ 4
Согласно Википедии:
В Python термин monkey patch относится только к динамическим модификациям класса или модуля во время выполнения, мотивированным намерением исправить существующий сторонний код в качестве обходного решения ошибки или функции, которые действуют не так, как вы хотите.