Что такое исправление обезьян? - PullRequest
467 голосов
/ 11 апреля 2011

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

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

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

Ответы [ 7 ]

455 голосов
/ 11 апреля 2011

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

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

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

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

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

  2. Если существует некоторая переменная или атрибут, который также указывает на функцию get_data к моменту ее замены, этот псевдоним не изменит своего значения и будет продолжать указывать на исходный get_data. (Почему? Python просто связывает имя get_data в вашем классе с другим функциональным объектом; на другие привязки имен это никак не влияет.)

342 голосов
/ 11 июля 2011

MonkeyPatch - это фрагмент кода Python, который расширяет или модифицирует другой код во время выполнения (обычно при запуске).

Простой пример выглядит так:

from SomeOtherProduct.SomeModule import SomeClass

def speak(self):
    return "ook ook eee eee eee!"

SomeClass.speak = speak

Источник: MonkeyPatch страница в вики Zope.

105 голосов
/ 14 декабря 2014

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

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

Пример использования

В документации 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, но я предполагаю, что он использует патч обезьян аналогичным образом.)

26 голосов
/ 11 апреля 2011

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

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

17 голосов
/ 11 апреля 2011

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

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

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

12 голосов
/ 12 апреля 2011

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

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

4 голосов
/ 14 января 2016

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

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

...