Що таке лачання мавп?


547

Я намагаюся зрозуміти, що таке патч мавп чи патч мавпи?

Це щось на зразок методів / операторів, які перевантажують чи делегують?

Чи має щось спільне з цими речами?


Я думаю, що визначення від Google є корисним і найзагальнішим:Monkey patching is a technique to add, modify, or suppress the default behavior of a piece of code at runtime without changing its original source code.
Чарлі Паркер

Відповіді:


522

Ні, це не так, як жодна з цих речей. Це просто динамічна заміна атрибутів під час виконання.

Наприклад, розглянемо клас, який має метод get_data. Цей метод здійснює зовнішній пошук (наприклад, у базі даних або веб-API), а також інші методи у класі називають його. Однак при тесті на одиницю ви не хочете залежати від зовнішнього джерела даних - тому ви динамічно замінюєте get_dataметод заглушкою, яка повертає деякі фіксовані дані.

Оскільки класи Python є змінними, а методи - це лише атрибути класу, ви можете робити це скільки завгодно - і, фактично, ви можете навіть замінити класи та функції в модулі точно так само.

Але, як зазначив коментатор , будьте обережні під час маніпулювання мавпами:

  1. Якщо нічого іншого, крім вашої логіки тестової логіки get_data, також дзвінки замінять замість оригіналу, замість оригіналу - що може бути добре чи погано. Тільки остерігайся.

  2. Якщо існує якась змінна або атрибут, яка також вказує на get_dataфункцію до моменту її заміни, цей псевдонім не змінить свого значення і продовжить вказувати на оригінал get_data. (Чому? Python просто перев'язує ім'я get_dataу вашому класі на якийсь інший функціональний об'єкт; інші прив’язки до імен взагалі не впливають.)


1
@LutzPrechelt просто, щоб було зрозуміло для мене, що ти маєш на увазі pointing to the original get_data function? Ви маєте на увазі, коли ви зберігаєте функцію всередині змінної, якщо хтось змінить цю функцію, змінна продовжить вказувати на стару?
fabriciorissetto

3
@fabriciorissetto: Ви зазвичай не змінюєте об'єкти функцій у Python. Під час мавпи-патча get_dataви прив'язуєте ім'я get_dataдо функції макету. Якщо якесь інше ім’я деінде в програмі пов'язане з функцією, яка раніше була відома як - get_data, для цього іншого імені нічого не зміниться.
Lutz Prechelt

1
@LutzPrechelt Не могли б ви пояснити трохи більше з цього приводу?
Кальвін Ку

Я думаю, що виправлення мавп може бути корисним особливо для налагодження та функцій декораторів чи об'єктів. Однак пам’ятати явне краще, ніж неявне, тому переконайтеся, що ваш код нечутливий до контексту, прочитайте «Перехід вважається шкідливим» тощо ...
aoeu256

Отже, це просто щось подібне до використання функції 'eval', куди ви можете вставити новий код під час виконання?
Wintermute

363

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

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

from SomeOtherProduct.SomeModule import SomeClass

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

SomeClass.speak = speak

Джерело: Сторінка MonkeyPatch на вікі Zope.


126

Що таке патч мавп?

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

Приклад використання

У документації на 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«S patchдекоратора буде менше помилок , ніж робити вище, що потребують більше рядків коди і , отже , більше можливостей для впровадження помилок . Я ще не переглянув код, mockале, думаю, він використовує аналогічний спосіб виправлення мавп.)


тож навантаження на мавпачок зберігає посилання на реальний метод? наприклад, що станеться, якщо хтось забуде крок "зберегти покажчик", він втрачається?
Томмі

3
@Tommy Якщо знижки на "переписаний" метод переходять до нуля - він збирається сміттям і, таким чином, "втрачається" протягом життя процесу (або, якщо модуль, в якому він виник, не перезавантажується, але це зазвичай не рекомендується).
Аарон Холл

33

За даними Вікіпедії :

У Python термін мавповий патч стосується лише динамічних модифікацій класу чи модуля під час виконання, мотивованих наміром виправити наявний сторонній код як вирішення помилки чи функції, яка не відповідає вашим бажанням.


16

По-перше: виправлення мавп - це зла злом (на мою думку).

Він часто використовується для заміни методу на рівні модуля або класу на власну реалізацію.

Найпоширенішою справою є додавання вирішення помилки в модулі або класі, коли ви не можете замінити вихідний код. У цьому випадку ви замінюєте "неправильний" код за допомогою патчу мавпи на реалізацію всередині власного модуля / пакета.


8
У випадку, якщо деякі модулі виправляють мавпи те саме: ви приречені.
Андреас Юнг

49
Хоча його потужність і справді може бути небезпечною, вона чудово підходить для тестування
dkrikun

1
Найпоширеніший випадок використання насправді для тестування, особливо тестування одиниць. Ви хочете перевірити лише свій код, тому ви закріпіть будь-який зовнішній дзвінок, щоб повернути очікуваний результат.
брокколі

1
це не зло, я використовую це для виправлення помилок у програмному забезпеченні інших людей, поки не вийде новий випуск замість того, щоб розпалювати та створити нову залежність.
Нуреттін

1
Патч-мавпа може бути виконана "чисто функціональним способом", а не зміненим, "контекстно-залежним", готоподібним способом, лише виконуючи виправлення всередині декораторів, які повертають нову виправлену версію вашого класу / методу (а не змінюють її). Дуже багато програмістів на C # / Java не знають про розробку, керовану REPL, тому вони кодують у своїх IDE, що вимагає, щоб все було визначено статично. Оскільки у C # / Java не було виправлення мавп, вони припускають її зло, коли бачать це у JavaScript, Smalltalk, Lisp, Python тощо ..., як це суперечить їхній статичній практиці розробки IDE.
aoeu256

13

Виправлення мавп можна робити лише в динамічних мовах, з яких пітон є хорошим прикладом. Зміна методу під час виконання замість оновлення визначення об'єкта є одним із прикладів; аналогічно, додавання атрибутів (будь то методи чи змінні) під час виконання вважається виправленням мавпи. Це часто робиться під час роботи з модулями, для яких у вас немає джерела, так що визначення об'єктів неможливо легко змінити.

Це вважається поганим, оскільки означає, що визначення об'єкта не повністю або точно описує, як він насправді веде себе.


Однак виправлення мавп може бути корисним, доки замість зміни існуючого об'єкта чи класу ви створите нову версію об’єкта з латками у членах всередині декоратора, який кричить "так, що я вас патчу".
aoeu256

Ви можете використовувати анотації на виправлених членах, щоб зберігати в виправленому члені, який декоратор використовувався для виправлення в патчах. Скажімо, у вас є непридатний декоратор, який створює нову непридатну версію функціонального об'єкта методом відміни. Ви можете помістити в декоратор поле латки, яке вказує на вашого непридатного декоратора.
aoeu256

5

Патч мавп - це повторне відкриття існуючих класів або методів у класі під час виконання та зміна поведінки, яку слід використовувати обережно, або використовувати її потрібно лише тоді, коли вам це справді потрібно.

Оскільки Python - це динамічна мова програмування, класи є змінними, тому ви можете їх знову відкрити, змінити або навіть замінити.


1

Що таке лачання мавп? Виправлення мавп - це техніка, яка використовується для динамічного оновлення поведінки фрагмента коду під час виконання.

Навіщо використовувати виправлення мавп? Це дозволяє нам змінювати або розширювати поведінку бібліотек, модулів, класів або методів під час виконання без фактичної зміни вихідного коду

Висновок Виправлення мавп - це класна техніка, і тепер ми навчилися робити це в Python. Однак, як ми обговорювали, у нього є свої недоліки, і їх слід обережно використовувати.

Для отримання додаткової інформації зверніться до [1]: https://medium.com/@nagillavenkatesh1234/monkey-patching-in-python-explained-with-examples-25eed0aea505

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.