Co to jest łatanie małp?

547

Próbuję zrozumieć, co to jest łatanie małp czy łata małp?

Czy to coś w rodzaju przeciążenia metod / operatorów lub delegowania?

Czy ma coś wspólnego z tymi rzeczami?

Siergiej Baszarow
źródło
Myślę, że definicja z Google jest przydatna i najbardziej ogólna: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.
Charlie Parker

Odpowiedzi:

522

Nie, to nie jest żadna z tych rzeczy. Jest to po prostu dynamiczna zamiana atrybutów w czasie wykonywania.

Rozważmy na przykład klasę, która ma metodę get_data. Ta metoda wykonuje zewnętrzne wyszukiwanie (na przykład w bazie danych lub interfejsie API sieci Web) i wywołują ją różne inne metody w klasie. Jednak w teście jednostkowym nie chcesz polegać na zewnętrznym źródle danych - więc dynamicznie zastępujesz get_datametodę kodem zwrotnym, który zwraca niektóre ustalone dane.

Ponieważ klasy Pythona są zmienne, a metody są tylko atrybutami klasy, możesz to zrobić tak, jak chcesz - a nawet zastąpić klasy i funkcje w module w dokładnie taki sam sposób.

Ale, jak zauważył komentator , należy zachować ostrożność podczas łączenia małp:

  1. Jeśli oprócz testowych wywołań logicznych coś innego get_data, wywoła również zastąpione łatką małpy zamiast oryginalnego - co może być dobre lub złe. Tylko uważaj.

  2. Jeśli istnieje jakaś zmienna lub atrybut, który również wskazuje get_datafunkcję do czasu jej zastąpienia, ten alias nie zmieni jej znaczenia i będzie nadal wskazywał na oryginał get_data. (Dlaczego? Python po prostu ponownie wiąże nazwę get_dataw klasie z jakimś innym obiektem funkcji; nie ma to żadnego wpływu na inne wiązania nazw).

Daniel Roseman
źródło
1
@LutzPrechelt tylko dla mnie, co masz na myśli pointing to the original get_data function? Czy masz na myśli to, że gdy funkcja jest przechowywana w zmiennej, jeśli ktoś ją zmieni, zmienna będzie nadal wskazywać na poprzednią?
fabriciorissetto
3
@fabriciorissetto: Zwykle nie zmieniasz obiektów funkcji w Pythonie. Kiedy wstawiasz małpę get_data, ponownie przypisujesz nazwę get_datado fałszywej funkcji. Jeśli jakaś inna nazwa gdzieś w programie jest powiązana z funkcją-dawniej-znana-jako- get_data, nic się nie zmieni dla tej innej nazwy.
Lutz Prechelt
1
@LutzPrechelt Czy możesz wyjaśnić coś więcej na ten temat?
Calvin Ku
Myślę, że łatanie małp może być przydatne szczególnie do debugowania oraz w dekoratorach lub funkcjach fabryk obiektów. Pamiętaj jednak, że jawne jest lepsze niż niejawne, więc upewnij się, że Twój kod nie uwzględnia kontekstu, przeczytaj „Goto uważane za szkodliwe” itp.
aoeu256 9.09.19
Jest to więc coś podobnego do używania funkcji „eval”, w której można wstawić nowy kod w czasie wykonywania?
wintermute
363

MonkeyPatch to fragment kodu w języku Python, który rozszerza lub modyfikuje inny kod w czasie wykonywania (zwykle podczas uruchamiania).

Prosty przykład wygląda następująco:

from SomeOtherProduct.SomeModule import SomeClass

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

SomeClass.speak = speak

Źródło: strona MonkeyPatch na wiki Zope.

Paolo
źródło
126

Co to jest łatka na małpy?

Mówiąc najprościej, łatanie małp wprowadza zmiany w module lub klasie podczas działania programu.

Przykład użycia

W dokumentacji Pand znajduje się przykład łatania małp:

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

Aby rozwiązać ten problem, najpierw importujemy nasz moduł:

import pandas as pd

Następnie tworzymy definicję metody, która istnieje niezwiązana i wolna poza zakresem jakichkolwiek definicji klas (ponieważ rozróżnienie między funkcją a niezwiązaną metodą jest dość pozbawione znaczenia, Python 3 eliminuje metodę niezwiązaną):

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]

Następnie po prostu dołączamy tę metodę do klasy, na której chcemy jej użyć:

pd.DataFrame.just_foo_cols = just_foo_cols # monkey-patch the DataFrame class

Następnie możemy użyć metody na instancji klasy i usunąć ją, gdy skończymy:

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

Zastrzeżenie dotyczące zmiany nazwy

Jeśli używasz manglingu nazw (przedrostek atrybutów z podwójnym podkreśleniem, który zmienia nazwę i którego nie polecam), będziesz musiał ręcznie nadać mu nazwę. Ponieważ nie polecam przekłamywania nazwisk, nie będę tego tutaj demonstrować.

Przykład testowy

Jak możemy wykorzystać tę wiedzę, na przykład podczas testowania?

Powiedzmy, że musimy zasymulować wywołanie pobierania danych do zewnętrznego źródła danych, które powoduje błąd, ponieważ w takim przypadku chcemy zapewnić prawidłowe zachowanie. Możemy małpować łatanie struktury danych, aby zapewnić takie zachowanie. (Więc używając podobnej nazwy metody, jak sugerował Daniel Roseman :)

import datasource

def get_data(self):
    '''monkey patch datasource.Structure with this to simulate error'''
    raise datasource.DataRetrievalError

datasource.Structure.get_data = get_data

A kiedy przetestujemy to pod kątem zachowania, które opiera się na tej metodzie powodującej błąd, jeśli zostanie poprawnie zaimplementowane, otrzymamy to zachowanie w wynikach testu.

Samo wykonanie powyższych czynności zmieni Structureobiekt na cały czas trwania procesu, więc będziesz chciał używać ustawień i rozbieżności w swoich najbardziej unikatowych miejscach, aby tego uniknąć, np .:

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

(Chociaż powyższy jest w porządku, będzie to prawdopodobnie lepszy pomysł, aby korzystać z mockbiblioteki załatać kod. mock„S patchdekorator byłoby mniej podatne na błędy niż robi to powyżej, który musiałby otrzymać więcej linii kodu, a tym samym większe możliwości wprowadzenia błędów Muszę jeszcze przejrzeć kod, mockale wyobrażam sobie, że używa łatania małp w podobny sposób.)

Aaron Hall
źródło
więc czy na monkeypatcherze spoczywa obowiązek przechowywania odniesienia do prawdziwej metody? np. co się stanie, jeśli zapomni się o kroku „zachowaj wskaźnik”, zostanie utracone?
Tommy
3
@ Tommy Jeśli przeliczenia na „nadpisaną” metodę idą do zera - są one usuwane, a zatem „tracone” przez cały czas trwania procesu (lub chyba, że ​​moduł, z którego pochodzą, zostaną ponownie załadowane, ale zwykle jest to odradzane).
Aaron Hall
33

Według Wikipedii :

W Pythonie termin łatka małpa odnosi się tylko do dynamicznych modyfikacji klasy lub modułu w czasie wykonywania, motywowanych intencją łatania istniejącego kodu innej firmy, jako obejście błędu lub funkcji, która nie działa tak, jak chcesz.

David Heffernan
źródło
16

Po pierwsze: łatanie małp to zły hack (moim zdaniem).

Często służy do zastępowania metody na poziomie modułu lub klasy niestandardową implementacją.

Najczęstszym przypadkiem użycia jest dodanie obejścia błędu w module lub klasie, gdy nie można zastąpić oryginalnego kodu. W takim przypadku zamieniasz „zły” kod poprzez łatanie małp na implementację wewnątrz własnego modułu / pakietu.

Andreas Jung
źródło
8
W przypadku, gdy niektóre moduły łatają to samo: jesteś skazany.
Andreas Jung,
49
Chociaż jego moc rzeczywiście może być niebezpieczna, świetnie
nadaje się
1
Najczęstszym przypadkiem użycia są testy, zwłaszcza testy jednostkowe. Chcesz przetestować tylko swój kod, więc załataj każde wywołanie zewnętrzne, aby zwrócić oczekiwany wynik.
brocoli
1
to nie jest złe, używam go do łatania błędów w oprogramowaniu innych ludzi, dopóki nie pojawi się nowa wersja zamiast rozwidlania i tworzenia nowej zależności.
nurettin
1
Łatowanie małp można wykonać w „czysto funkcjonalny sposób”, a nie w sposób zmienny, „kontekstowy”, podobny do goto, wykonując jedynie łatki w dekoratorach, które zwracają nową łataną wersję twojej klasy / metody (zamiast ją modyfikować). Wielu programistów C # / Java nie wie o rozwoju opartym na REPL, więc kodują w swoich IDE, co wymaga statycznego zdefiniowania wszystkiego. Ponieważ C # / Java nie miało łatania małp, zakładają, że to zło, gdy widzą go w JavaScript, Smalltalk, Lisp, Python itp., Ponieważ jest to sprzeczne z ich statyczną praktyką programowania opartą na IDE.
aoeu256
13

Łatowanie małp można wykonywać tylko w dynamicznych językach, których dobrym przykładem jest Python. Przykładem jest zmiana metody w czasie wykonywania zamiast aktualizacji definicji obiektu; podobnie dodawanie atrybutów (metod lub zmiennych) w czasie wykonywania jest uważane za łatanie małp. Są to często wykonywane podczas pracy z modułami, dla których nie masz źródła, tak że definicji obiektów nie można łatwo zmienić.

Uznaje się to za złe, ponieważ oznacza, że ​​definicja obiektu nie opisuje w pełni ani dokładnie, jak faktycznie się zachowuje.

Aaron Dufour
źródło
Jednak łatanie małp może być przydatne, o ile zamiast modyfikować istniejący obiekt lub klasę, tworzysz nową wersję obiektu z łatkami w elementach dekoratora, który krzyczy „hej, zamierzam cię załatać”.
aoeu256
Za pomocą adnotacji na łatanych elementach można przechowywać w łatanym elemencie, którego dekorator został użyty do łatania łatek. Załóżmy, że masz dekorator, który można cofnąć, i tworzy nową wersję obiektu funkcji za pomocą metody cofania. Możesz umieścić w dekoratorze pole patchera wskazujące na nieodwracalnego dekoratora.
aoeu256
5

Patchowanie małp powoduje ponowne otwarcie istniejących klas lub metod w środowisku uruchomieniowym i zmianę zachowania, które należy stosować ostrożnie, lub należy go używać tylko wtedy, gdy jest to naprawdę potrzebne.

Ponieważ Python jest dynamicznym językiem programowania, klasy można modyfikować, dzięki czemu można je ponownie otworzyć i zmodyfikować, a nawet zastąpić.

kamal
źródło
1

Co to jest łatanie małp? Patchowanie małp jest techniką używaną do dynamicznej aktualizacji zachowania fragmentu kodu w czasie wykonywania.

Dlaczego warto korzystać z łatania małp? Pozwala nam modyfikować lub rozszerzać zachowanie bibliotek, modułów, klas lub metod w czasie wykonywania bez faktycznej modyfikacji kodu źródłowego

Wniosek Patchowanie małp jest fajną techniką i teraz nauczyliśmy się, jak to robić w Pythonie. Jednak, jak omówiliśmy, ma on swoje wady i należy go używać ostrożnie.

Aby uzyskać więcej informacji, zapoznaj się z [1]: https://medium.com/@nagillavenkatesh1234/monkey-patching-in-python-explained-with-examples-25eed0aea505

akash kumar
źródło