Czym jest monkey patching w Pythonie?
Termin monkey patching w Pythonie odnosi się do dynamicznych modyfikacji klasy lub modułu w czasie uruchamiania. Oznacza to, że monkey patch to fragment kodu, który rozszerza lub modyfikuje inny kod podczas wykonania programu. Monkey patching jest możliwy tylko w językach dynamicznych, których dobrym przykładem jest tutaj Python. Polega on na ponownym otwieraniu istniejących klas lub metod podczas uruchamiania kodu i zmienianiu ich zachowania. Należy jednak postępować tutaj ostrożnie oraz korzystać z tego tylko, jeśli naprawdę musisz.
Ponieważ Python jest dynamicznym językiem programowania, to klasy są modyfikowalne. Można je dzięki temu ponownie otwierać i zmieniać, czy też nawet zastępować czymś innym. Monkey patching służy również do zastępowania lub rozszerzania metody na poziomie modułu, lub klasy, za pomocą niestandardowej implementacji.
Zaimplementujmy więc to:
class MonkeyPatch:
def __init__(self, num):
self.num = num
def addition(self, other):
return (self.num + other)
obj = MonkeyPatch(10)
obj.addition(20)
Wynik:
30
Jak widać poniżej, tylko metody __init__
oraz addition
są dostępne dla powyższego obiektu klasy (możesz również użyć dir(obj)
, aby uzyskać składowe obiektu).
import inspect
inspect.getmembers(obj, predicate=inspect.ismethod)
Wynik:
[('__init__',
<bound method MonkeyPatch.__init__ of <__main__.MonkeyPatch object at 0x7f32495d7c50>>),
('addition',
<bound method MonkeyPatch.addition of <__main__.MonkeyPatch object at 0x7f32495d7c50>>)]
W powyższym kodzie zdefiniowaliśmy klasę MonkeyPatch
, która ma metodę addition
. Następnie dodajemy nową metodę do klasy MonkeyPatch
, która może wyglądać tak:
def subtraction(self, num2):
return self.num - num2
Gdy dodajemy powyższą metodę do klasy MonkeyPatch
, to umieszczamy funkcję subtraction
w klasie MonkeyPatch
za pomocą instrukcji przypisania:
MonkeyPatch.subtraction = subtraction
Nowa funkcja subtraction będzie dostępna dla wszystkich istniejących i nowych instancji. Przejdźmy do praktyki:
import inspect
inspect.getmembers(obj, predicate=inspect.ismethod)
Wynik:
[('__init__',
<bound method MonkeyPatch.__init__ of <__main__.MonkeyPatch object at 0x7f28186b8c88>>),
('addition',
<bound method MonkeyPatch.addition of <__main__.MonkeyPatch object at 0x7f28186b8c88>>),
('subtraction',
<bound method subtraction of <__main__.MonkeyPatch object at 0x7f28186b8c88>>)]
Czy zauważyliście, że istniejący obiekt obj
, który zdefiniowaliśmy wcześniej, zawierał nowo utworzoną funkcję? Sprawdźmy, czy będzie ona działać dla istniejącej instancji:
>>> obj.subtraction(1) # Working as expected
9
>>> obj_1 = MonkeyPatch(10) # create some new object
>>> obj_1.subtraction(2)
8
obj.subtraction
działa zgodnie z oczekiwaniami, ale należy pamiętać, że jeśli istnieje już metoda o tej samej nazwie, to spowoduje ona zmianę zachowania istniejącej metody.
O czym warto pamiętać
Najlepszą rzeczą byłoby niekorzystanie z monkey patchingu. Jeżeli chcesz zmienić zachowanie, możesz zdefiniować klasy podrzędne. Mimo to, jeśli potrzebujesz monkey patchingu, to postępuj zgodnie z poniższymi zasadami:
- Używaj go, jeśli masz naprawdę dobry powód (np. tymczasowa krytyczna poprawka)
- Napisz odpowiednią dokumentację opisującą powód, dla którego użyłeś monkey patchingu
- Dokumentacja powinna również zawierać informacje o usunięciu monkey patcha i to, na co należy zwrócić uwagę. Wiele takich rozwiązań jest tymczasowa, więc usunięcie ich powinno być proste.
- Postaraj się, aby monkey patch był jak najbardziej wyeksponowany. Umieść też kod tego rozwiązania w osobnych plikach.
Podsumowanie
Oto, jak powinno się używać monkey patchingu w Pythonie. Korzystanie z tego rozwiązania ma jednak swoje wady i należy do tego podchodzić ostrożnie.
Użycie monkey patchingu często oznacza to złą architekturę aplikacji, no i nie jest dobrą decyzją z punktu widzenia projektu, ponieważ stwarza rozbieżność między oryginalnym kodem źródłowym na dysku a obserwowanym zachowaniem. Co więcej, może to być bardzo mylące podczas rozwiązywania pewnych problemów.
Oryginał tekstu w języku angielskim możesz przeczytać tutaj.