Przeczytałem, że w Pythonie można dodać metodę do istniejącego obiektu (tj. Nie w definicji klasy).
Rozumiem, że nie zawsze jest to dobre. Ale jak to zrobić?
źródło
Przeczytałem, że w Pythonie można dodać metodę do istniejącego obiektu (tj. Nie w definicji klasy).
Rozumiem, że nie zawsze jest to dobre. Ale jak to zrobić?
W Pythonie istnieje różnica między funkcjami a powiązanymi metodami.
>>> def foo():
... print "foo"
...
>>> class A:
... def bar( self ):
... print "bar"
...
>>> a = A()
>>> foo
<function foo at 0x00A98D70>
>>> a.bar
<bound method A.bar of <__main__.A instance at 0x00A9BC88>>
>>>
Metody powiązane zostały „powiązane” (jak opisowe) z instancją i ta instancja będzie przekazywana jako pierwszy argument przy każdym wywołaniu metody.
Odwołania, które są atrybutami klasy (w przeciwieństwie do instancji) są nadal niezwiązane, więc możesz zmodyfikować definicję klasy, kiedy tylko chcesz:
>>> def fooFighters( self ):
... print "fooFighters"
...
>>> A.fooFighters = fooFighters
>>> a2 = A()
>>> a2.fooFighters
<bound method A.fooFighters of <__main__.A instance at 0x00A9BEB8>>
>>> a2.fooFighters()
fooFighters
Wcześniej zdefiniowane instancje są również aktualizowane (o ile same nie zastąpiły atrybutu):
>>> a.fooFighters()
fooFighters
Problem pojawia się, gdy chcesz dołączyć metodę do pojedynczego wystąpienia:
>>> def barFighters( self ):
... print "barFighters"
...
>>> a.barFighters = barFighters
>>> a.barFighters()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: barFighters() takes exactly 1 argument (0 given)
Funkcja nie jest automatycznie powiązana, gdy jest dołączona bezpośrednio do instancji:
>>> a.barFighters
<function barFighters at 0x00A98EF0>
Aby to powiązać, możemy użyć funkcji MethodType w module typów :
>>> import types
>>> a.barFighters = types.MethodType( barFighters, a )
>>> a.barFighters
<bound method ?.barFighters of <__main__.A instance at 0x00A9BC88>>
>>> a.barFighters()
barFighters
Tym razem nie dotyczy to innych instancji klasy:
>>> a2.barFighters()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: A instance has no attribute 'barFighters'
Więcej informacji można znaleźć, czytając o deskryptorach i programowaniu metaklasy .
MethodType
, ręcznie wywołaj protokół deskryptora i niech funkcja wygeneruje twoją instancję:barFighters.__get__(a)
tworzy powiązaną metodębarFighters
powiązania za
.descriptor protocol
vs tworzą naMethodType
bok być może nieco bardziej czytelne.classmethod
istaticmethod
oraz inne deskryptory też. Pozwala to uniknąć zaśmiecania przestrzeni nazw kolejnym importem.a.barFighters = barFighters.__get__(a)
Nowy moduł jest przestarzały od Pythona 2.6 i usunięty w 3.0, użyj typów
patrz http://docs.python.org/library/new.html
W poniższym przykładzie celowo usunąłem wartość zwracaną z
patch_me()
funkcji. Myślę, że podanie wartości zwracanej może sprawić, że uwierzysz, że łatka zwraca nowy obiekt, co nie jest prawdą - modyfikuje przychodzący. Prawdopodobnie może to ułatwić bardziej zdyscyplinowane korzystanie z monkeypatchingu.źródło
Przedmowa - uwaga na temat zgodności: inne odpowiedzi mogą działać tylko w Pythonie 2 - ta odpowiedź powinna działać idealnie w Pythonie 2 i 3. Jeśli piszesz tylko w Pythonie 3, możesz pominąć dziedziczenie
object
, ale w przeciwnym razie kod powinien pozostać ten sam .Tak, jest to możliwe - ale nie zalecane
Nie polecam tego. To jest zły pomysł. Nie rób tego
Oto kilka powodów:
Dlatego sugeruję, abyś tego nie robił, chyba że masz naprawdę dobry powód. Zdecydowanie lepiej jest zdefiniować poprawną metodę w definicji klasy, lub mniej korzystnie, aby łatać klasę bezpośrednio, tak jak to:
Ponieważ jest to pouczające, pokażę ci kilka sposobów na zrobienie tego.
Jak można to zrobić
Oto kod instalacyjny. Potrzebujemy definicji klasy. Można go zaimportować, ale to naprawdę nie ma znaczenia.
Utwórz instancję:
Utwórz metodę, aby ją dodać:
Metoda naught (0) - użyj metody deskryptora,
__get__
Kropkowane wyszukiwania funkcji wywołują
__get__
metodę funkcji z instancją, wiążąc obiekt z metodą i tworząc w ten sposób „powiązaną metodę”.i teraz:
Metoda pierwsza - types.MethodType
Najpierw zaimportuj typy, z których otrzymamy konstruktor metod:
Teraz dodajemy metodę do instancji. Aby to zrobić, potrzebujemy konstruktora MethodType z
types
modułu (który zaimportowaliśmy powyżej).Podpis argumentu dla typów.MethodType to
(function, instance, class)
:i użycie:
Metoda druga: wiązanie leksykalne
Najpierw tworzymy funkcję otoki, która wiąże metodę z instancją:
stosowanie:
Metoda trzecia: funkools.partial
Funkcja częściowa stosuje pierwsze argumenty do funkcji (i opcjonalnie argumenty słów kluczowych), a później można je wywołać z pozostałymi argumentami (i nadpisując argumenty słów kluczowych). A zatem:
Ma to sens, gdy weźmie się pod uwagę, że powiązane metody są częściowymi funkcjami instancji.
Funkcja niezwiązana jako atrybut obiektu - dlaczego to nie działa:
Jeśli spróbujemy dodać metodę_próbki w taki sam sposób, w jaki moglibyśmy dodać ją do klasy, jest ona niezwiązana z instancją i nie przyjmuje domyślnego „ja” jako pierwszego argumentu.
Możemy sprawić, by funkcja niezwiązana działała, jawnie przekazując instancję (lub cokolwiek innego, ponieważ ta metoda tak naprawdę nie używa
self
zmiennej argumentu), ale nie byłaby zgodna z oczekiwaną sygnaturą innych instancji (jeśli łatamy małpy to wystąpienie):Wniosek
Znasz teraz kilka sposobów, aby to zrobić, ale z całą powagą - nie rób tego.
źródło
__get__
Metoda wymaga również klasę jako następnego parametru:sample_method.__get__(foo, Foo)
.Myślę, że powyższe odpowiedzi pominęły kluczowy punkt.
Zróbmy klasę z metodą:
Teraz zagrajmy z nim w ipython:
Ok, m () w jakiś sposób staje się niezwiązany metoda A . Ale czy to naprawdę tak?
Okazuje się, że m () to tylko funkcja, do której odniesienie jest dodane do słownika klasy A - nie ma magii. Więc dlaczego Am daje nam sposób niezwiązany? To dlatego, że kropka nie jest tłumaczona na proste wyszukiwanie w słowniku. Jest to de facto wywołanie klasy A .__ __.__ getattribute __ (A, 'm'):
Teraz nie jestem pewien, dlaczego ostatnia linia jest drukowana dwukrotnie, ale nadal jest jasne, co się tam dzieje.
Teraz domyślna funkcja __getattribute__ polega na sprawdzeniu, czy atrybut jest tak zwanym deskryptorem, czy nie, tj. Czy implementuje specjalną metodę __get__. Jeśli implementuje tę metodę, zwracany jest wynik wywołania tej metody __get__. Wracając do pierwszej wersji naszej klasy A , mamy to:
A ponieważ funkcje Pythona implementują protokół deskryptora, jeśli są wywoływane w imieniu obiektu, wiążą się z tym obiektem za pomocą metody __get__.
Ok, więc jak dodać metodę do istniejącego obiektu? Zakładając, że nie masz nic przeciwko łataniu klasy, jest to tak proste jak:
Wtedy Bm „staje się” metodą niezwiązaną dzięki magii deskryptorów.
A jeśli chcesz dodać metodę tylko do jednego obiektu, musisz sam emulować maszynerię, używając types.MethodType:
Tak poza tym:
źródło
W Pythonie łatanie małp na ogół działa poprzez nadpisanie podpisu klasy lub funkcji własnym. Poniżej znajduje się przykład z Wiki Zope :
Ten kod nadpisze / utworzy metodę o nazwie speak na klasie. W ostatnim poście Jeffa Atwooda na temat łatania małp . Pokazuje przykład w C # 3.0, który jest bieżącym językiem, którego używam do pracy.
źródło
Możesz użyć lambda do powiązania metody z instancją:
Wynik:
źródło
Istnieją co najmniej dwa sposoby dołączenia metody do instancji bez
types.MethodType
:1:
2:
Przydatne linki:
Model danych - wywoływanie deskryptorów
Deskryptor Poradnik - wywoływanie deskryptorów
źródło
To, czego szukasz, to
setattr
wierzę. Użyj tego, aby ustawić atrybut obiektu.źródło
A
, a nie instancjęa
.setattr(A,'printme',printme)
zamiast po prostuA.printme = printme
?Ponieważ pytanie dotyczy wersji innych niż Python, oto JavaScript:
źródło
Konsolidacja odpowiedzi Jasona Pratta i społeczności wiki, z uwzględnieniem wyników różnych metod wiązania:
Szczególnie zwrócić uwagę w jaki sposób dodanie funkcji wiążącej jako metoda klasy dzieł , ale zakres przedstawieniu jest nieprawidłowy.
Osobiście wolę zewnętrzną trasę funkcji ADDMETHOD, ponieważ pozwala mi dynamicznie przypisywać nowe nazwy metod również w iteratorze.
źródło
addmethod
przepisany w następujący sposóbdef addmethod(self, method, name): self.__dict__[name] = types.MethodType( method, self )
rozwiązuje problemTo właściwie dodatek do odpowiedzi „Jason Pratt”
Chociaż odpowiedź Jadesa działa, działa ona tylko wtedy, gdy chce się dodać funkcję do klasy. Nie działało to dla mnie, gdy próbowałem ponownie załadować już istniejącą metodę z pliku kodu źródłowego .py.
Znalezienie obejścia zajęło mi od wieków, ale sztuczka wydaje się prosta ... 1. zaimportuj kod z pliku kodu źródłowego 2. i wymuś przeładowanie 3. typy użycia. Funkcja Typ (...) do konwersji zaimportowaną i powiązaną metodę z funkcją można również przekazać bieżącym zmiennym globalnym, ponieważ ponownie załadowana metoda będzie w innej przestrzeni nazw 4. teraz można kontynuować zgodnie z sugestią „Jason Pratt”, używając typów.MethodType (... )
Przykład:
źródło
Jeśli to może pomóc, niedawno wydałem bibliotekę Python o nazwie Gorilla, aby ułatwić łatanie małp.
Korzystanie z funkcji
needle()
do łatania modułu o nazwieguineapig
przebiega następująco:Ale zajmuje się również bardziej interesującymi przypadkami użycia, jak pokazano w FAQ z dokumentacji .
Kod jest dostępny na GitHub .
źródło
To pytanie zostało otwarte lata temu, ale hej, istnieje prosty sposób na symulację powiązania funkcji z instancją klasy za pomocą dekoratorów:
Tam, gdy przekażesz funkcję i instancję do dekoratora spoiwa, utworzy ona nową funkcję, z tym samym obiektem kodu co pierwszy. Następnie dane wystąpienie klasy jest przechowywane w atrybucie nowo utworzonej funkcji. Dekorator zwraca (trzecią) funkcję wywołującą automatycznie skopiowaną funkcję, podając instancję jako pierwszy parametr.
Podsumowując, otrzymujesz funkcję symulującą jej wiązanie z instancją klasy. Pozostawienie oryginalnej funkcji niezmienionej.
źródło
To, co napisał Jason Pratt, jest poprawne.
Jak widać, Python nie uważa, że b () różni się od a (). W Pythonie wszystkie metody są tylko zmiennymi, które okazały się funkcjami.
źródło
Test
, a nie jej instancję.Wydaje mi się dziwne, że nikt nie wspomniał, że wszystkie metody wymienione powyżej tworzą odwołanie do cyklu między dodaną metodą a instancją, powodując, że obiekt jest trwały aż do wyrzucania elementów bezużytecznych. Była stara sztuczka polegająca na dodaniu deskryptora przez rozszerzenie klasy obiektu:
źródło
Dzięki temu możesz użyć wskaźnika własnego
źródło