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!!
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!!
Odpowiedzi:
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 bobyi 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"
funcTypei dlaczego jest konieczne.
funcType = types.MethodType(po zaimportowaniu types) zamiast funcType = type(Dog.bark).
__get__funkcję, aby powiązać ją z instancją.
Musisz użyć MethodType z typesmodułu. Celem MethodTypejest nadpisanie metod na poziomie instancji (aby selfbył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!!
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średnictwem self.
klasa Pies:
def bark (self):
drukuj "Hau"
def new_bark (self):
drukuj "Woof Woof"
foo = Pies ()
# "Hau"
foo.bark ()
# zamień kory na new_bark tylko dla tego obiektu
foo.bark = new_bark .__ get __ (foo, Dog)
foo.bark ()
# "Woof Woof"
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łanie x in instancezignoruje to i użyje type(instance).__contains__(instance, x)zamiast tego. Dotyczy to wszystkich magicznych metod określonych w modelu danych Pythona .
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!!
bobyJeś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życie self.
patchingi to jest właściwy sposób, aby to zrobić (np. boby = Dog()I boby.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.partialtu 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!!
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ą
calling original bark
woof
calling the new bark
wooOOOoof
calling another new
nowoof
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
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
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()