Что такое патч обезьяны?
Проще говоря, патч обезьяны вносит изменения в модуль или класс во время работы программы.
Пример использования
В документации 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
Предупреждение для искажения имени
Если вы используете искажение имени (добавление префикса к атрибуту с двойным подчеркиванием, которое меняет имя,и что я не рекомендую) вам придется вручную поменять имя, если вы сделаете это.Поскольку я не рекомендую искажение имен, я не буду демонстрировать это здесь.
Пример тестирования
Как мы можем использовать эти знания, например, при тестировании?
Скажем, нам нужно смоделировать вызов извлечения данных из внешнего источника данных, который приводит к ошибке, потому что мы хотим обеспечить правильное поведение в таком случае.Мы можем обезопасить структуру данных, чтобы обеспечить такое поведение.(Таким образом, используя аналогичное имя метода, предложенное Дэниелом Роузманом:)
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
на весь срок службы процесса, поэтому вы захотите использовать настройки и разрывы в тестах юнитов, чтобы избежать этого, например:
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
*1042* был бы менее подвержен ошибкам, чем выполнениевыше, что потребовало бы больше строк кода и, следовательно, больше возможностей для появления ошибок. Мне еще предстоит просмотреть код в mock
, но я предполагаю, что он использует патч обезьян аналогичным образом.)