Что такое патч обезьяны?

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

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

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

Ответ 1

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

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

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

Ответ 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

Предостережение для создания имен

Если вы используете манипулирование именами (префикс атрибутов с двойным подчеркиванием, который изменяет имя и который я не рекомендую), вы должны будете вручную называть mangle, если вы это сделаете. Поскольку я не рекомендую манипулировать именами, я не буду показывать это здесь.

Пример тестирования

Как мы можем использовать это знание, например, при тестировании?

Скажем, нам нужно смоделировать вызов извлечения данных на внешний источник данных, что приводит к ошибке, потому что мы хотим обеспечить правильное поведение в таком случае. Мы можем обезвредить структуру данных, чтобы обеспечить такое поведение. (Таким образом, используя аналогичное имя метода, предложенное Даниэлем Роземэном:)

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

Согласно Wikipedia:

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

Ответ 5

Во-первых: патч обезьяны - это злой хак (на мой взгляд).

Он часто используется для замены метода на модуле или уровне класса с помощью специальной реализации.

Наиболее распространенным методом usecase является добавление обходного пути для ошибки в модуле или классе, если вы не можете заменить исходный код. В этом случае вы заменяете "неправильный" код через патч обезьяны с реализацией внутри вашего собственного модуля/пакета.

Ответ 6

Патч обезьяны может выполняться только в динамических языках, примером чего является python. Одним из примеров является изменение метода во время выполнения, а не обновление определения объекта, аналогичным образом добавление атрибутов (независимо от того, являются ли методы или переменные) во время выполнения, считается исправление обезьяны. Они часто выполняются при работе с модулями, для которых у вас нет источника, так что определения объектов не могут быть легко изменены.

Это считается плохим, потому что это означает, что определение объекта не полностью или точно не описывает, как оно себя ведет.

Ответ 7

Патч обезьяны снова открывает существующие классы или методы в классе во время выполнения и изменяет поведение, которое следует использовать осторожно, или вы должны использовать его только тогда, когда вам действительно нужно.

Поскольку Python является языком динамического программирования, классы изменяемы, поэтому вы можете их повторно открывать и изменять или даже заменять.