Czy w Pythonie jest sposób na przesłonięcie metody klasy na poziomie instancji? Na przykład:
class Dog:
def bark(self):
print "WOOF"
boby = Dog()
boby.bark() # WOOF
# METHOD OVERRIDE
boby.bark() # WoOoOoF!!
Nie rób tego tak, jak pokazano. Twój kod staje się nieczytelny, gdy monkeyp patrzysz na instancję, która różni się od klasy.
Nie można debugować kodu z monkeypatchami.
Kiedy znajdziesz błąd w programie boby
i print type(boby)
, zobaczysz, że (a) jest to pies, ale (b) z jakiegoś niejasnego powodu nie szczeka poprawnie. To jest koszmar. Nie rób tego.
Zrób to zamiast tego.
class Dog:
def bark(self):
print "WOOF"
class BobyDog( Dog ):
def bark( self ):
print "WoOoOoF!!"
otherDog= Dog()
otherDog.bark() # WOOF
boby = BobyDog()
boby.bark() # WoOoOoF!!
Tak, to możliwe:
class Dog: def bark(self): print "Woof" def new_bark(self): print "Woof Woof" foo = Dog() funcType = type(Dog.bark) # "Woof" foo.bark() # replace bark with new_bark for this object only foo.bark = funcType(new_bark, foo, Dog) foo.bark() # "Woof Woof"
źródło
funcType
i dlaczego jest konieczne.funcType = types.MethodType
(po zaimportowaniutypes
) zamiastfuncType = type(Dog.bark)
.__get__
funkcję, aby powiązać ją z instancją.Musisz użyć MethodType z
types
modułu. CelemMethodType
jest nadpisanie metod na poziomie instancji (abyself
były dostępne w nadpisywanych metodach).Zobacz poniższy przykład.
import types class Dog: def bark(self): print "WOOF" boby = Dog() boby.bark() # WOOF def _bark(self): print "WoOoOoF!!" boby.bark = types.MethodType(_bark, boby) boby.bark() # WoOoOoF!!
źródło
Aby wyjaśnić doskonałą odpowiedź @ codelogic, proponuję bardziej wyraźne podejście. Jest to ta sama technika, którą
.
operator robi dokładnie, aby powiązać metodę klasy, gdy uzyskujesz do niej dostęp jako atrybut instancji, z tym wyjątkiem, że Twoja metoda będzie w rzeczywistości funkcją zdefiniowaną poza klasą.Pracując z kodem @ codelogic, jedyną różnicą jest sposób powiązania metody. Używam faktu, że funkcje i metody nie są deskryptorami danych w Pythonie i wywołuję tę
__get__
metodę. Zwróć szczególną uwagę, że zarówno oryginał, jak i zamiennik mają identyczne podpisy, co oznacza, że możesz napisać zamiennik jako metodę pełnej klasy, uzyskując dostęp do wszystkich atrybutów instancji za pośrednictwemself
.Przypisując powiązaną metodę do atrybutu instancji, utworzyłeś niemal pełną symulację zastępowania metody. Jedną z przydatnych funkcji, której brakuje, jest dostęp do wersji bez argumentów
super
, ponieważ nie jesteś w definicji klasy. Inną rzeczą jest to, że__name__
atrybut twojej metody związanej nie przyjmie nazwy funkcji, którą przesłania, jak w definicji klasy, ale nadal możesz ustawić ją ręcznie. Trzecia różnica polega na tym, że metoda powiązana ręcznie jest zwykłym odwołaniem do atrybutu, który po prostu jest funkcją..
Operator nic nie robi, ale pobrać to odwołanie. Z drugiej strony podczas wywoływania zwykłej metody z wystąpienia proces wiązania tworzy za każdym razem nową powiązaną metodę.Nawiasem mówiąc, jedynym powodem, dla którego to działa, jest to, że atrybuty instancji zastępują deskryptory niebędące danymi . Deskryptory danych mają
__set__
metody, których (na szczęście dla Ciebie) nie. Deskryptory danych w klasie mają w rzeczywistości pierwszeństwo przed atrybutami instancji. Dlatego możesz przypisać do właściwości: ich__set__
metoda jest wywoływana podczas próby przypisania. Osobiście lubię pójść o krok dalej i ukryć rzeczywistą wartość podstawowego atrybutu w instancji__dict__
, gdzie jest ona niedostępna w normalny sposób, dokładnie dlatego, że właściwość ją przesłania.Należy również pamiętać, że nie ma to sensu w przypadku metod magicznych (podwójnego podkreślenia) . Metody magiczne można oczywiście w ten sposób nadpisać, ale operacje, które ich używają, patrzą tylko na typ. Na przykład możesz ustawić
__contains__
coś specjalnego w swojej instancji, ale wywołaniex in instance
zignoruje to i użyjetype(instance).__contains__(instance, x)
zamiast tego. Dotyczy to wszystkich magicznych metod określonych w modelu danych Pythona .źródło
class Dog: def bark(self): print "WOOF" boby = Dog() boby.bark() # WOOF # METHOD OVERRIDE def new_bark(): print "WoOoOoF!!" boby.bark = new_bark boby.bark() # WoOoOoF!!
boby
Jeśli potrzebujesz, możesz użyć zmiennej wewnątrz funkcji. Ponieważ nadpisujesz metodę tylko dla tego jednego obiektu instancji, ten sposób jest prostszy i daje dokładnie taki sam efekt jak użycieself
.źródło
patching
i to jest właściwy sposób, aby to zrobić (np.boby = Dog()
Iboby.bark = new_bark
). Jest to niezwykle przydatne w testach jednostkowych dla kontroli. Więcej wyjaśnień znajdziesz na tryolabs.com/blog/2013/07/05/run-time-method-patching-python (przykłady) - nie Nie jestem powiązany z linkowaną witryną lub autorem.Ponieważ nikt
functools.partial
tu nie wspomina :from functools import partial class Dog: name = "aaa" def bark(self): print("WOOF") boby = Dog() boby.bark() # WOOF def _bark(self): print("WoOoOoF!!") boby.bark = partial(_bark, boby) boby.bark() # WoOoOoF!!
źródło
Ponieważ funkcje są obiektami pierwszej klasy w Pythonie, możesz je przekazać podczas inicjalizacji obiektu klasy lub nadpisać je w dowolnym momencie dla danej instancji klasy:
class Dog: def __init__(self, barkmethod=None): self.bark=self.barkp if barkmethod: self.bark=barkmethod def barkp(self): print "woof" d=Dog() print "calling original bark" d.bark() def barknew(): print "wooOOOoof" d1=Dog(barknew) print "calling the new bark" d1.bark() def barknew1(): print "nowoof" d1.bark=barknew1 print "calling another new" d1.bark()
a wyniki są
źródło
Chociaż podobał mi się pomysł dziedziczenia od S. Lott i zgadzam się z rzeczą `` typ (a) '', ale ponieważ funkcje również mają dostępne atrybuty, myślę, że można nimi zarządzać w ten sposób:
class Dog: def __init__(self, barkmethod=None): self.bark=self.barkp if barkmethod: self.bark=barkmethod def barkp(self): """original bark""" print "woof" d=Dog() print "calling original bark" d.bark() print "that was %s\n" % d.bark.__doc__ def barknew(): """a new type of bark""" print "wooOOOoof" d1=Dog(barknew) print "calling the new bark" d1.bark() print "that was %s\n" % d1.bark.__doc__ def barknew1(): """another type of new bark""" print "nowoof" d1.bark=barknew1 print "another new" d1.bark() print "that was %s\n" % d1.bark.__doc__
a wynik to:
calling original bark woof that was original bark calling the new bark wooOOOoof that was a new type of bark another new nowoof that was another type of new bark
źródło
Drogi, to nie jest przesłonięcie, po prostu wywołujesz tę samą funkcję dwa razy z obiektem. Zasadniczo nadpisywanie jest związane z więcej niż jedną klasą. jeśli ta sama metoda podpisu istnieje w różnych klasach, wtedy to, która funkcja wywołuje tę funkcję, zdecyduje, który obiekt ją wywoła. Zastępowanie jest możliwe w Pythonie, gdy tworzysz więcej niż jedną klasę, zapisuje te same funkcje i jeszcze jedną rzecz do udostępnienia, że przeciążanie jest niedozwolone w Pythonie
źródło
Uważam, że jest to najdokładniejsza odpowiedź na pierwotne pytanie
https://stackoverflow.com/a/10829381/7640677
import a def _new_print_message(message): print "NEW:", message a.print_message = _new_print_message import b b.execute()
źródło