Вопрос-Ответ

What is monkey patching?

Что такое обезьянье исправление?

Я пытаюсь понять, что такое monkey patching или обезьяний патч?

Это что-то вроде перегрузки методов / операторов или делегирования?

Имеет ли это что-нибудь общее с этими вещами?

Переведено автоматически
Ответ 1

Нет, это не похоже ни на что из вышеперечисленного. Это просто динамическая замена атрибутов во время выполнения.

Для примера рассмотрим класс, у которого есть метод get_data. Этот метод выполняет внешний поиск (например, в базе данных или веб-API), и его вызывают различные другие методы в классе. Однако в модульном тестировании вы не хотите зависеть от внешнего источника данных - поэтому вы динамически заменяете get_data метод заглушкой, которая возвращает некоторые фиксированные данные.

Поскольку классы Python изменяемы, а методы являются всего лишь атрибутами класса, вы можете делать это сколько угодно - и, фактически, вы даже можете заменять классы и функции в модуле точно таким же образом.

Но, как указал комментатор, будьте осторожны при monkeypatching:


  1. Если что-то еще, кроме вашей тестовой логики, также вызывает get_data, это также вызовет вашу замену, исправленную monkey, а не оригинал - что может быть хорошим или плохим. Просто будьте осторожны.


  2. Если существует какая-либо переменная или атрибут, который также указывает на 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 библиотеку для исправления кода. mockpatchдекоратор был бы менее подвержен ошибкам, чем выполнение вышеописанного, что потребовало бы большего количества строк кода и, следовательно, больше возможностей для внесения ошибок. Мне еще предстоит просмотреть код в mock, но я предполагаю, что он использует обезьянье исправление аналогичным образом.)

Ответ 4

Согласно Википедии:


В Python термин monkey patch относится только к динамическим модификациям класса или модуля во время выполнения, мотивированным намерением исправить существующий сторонний код в качестве обходного решения ошибки или функции, которые действуют не так, как вы хотите.


python