Gibt es in Python eine Möglichkeit, eine Klassenmethode auf Instanzebene zu überschreiben? Beispielsweise:
class Dog:
def bark(self):
print "WOOF"
boby = Dog()
boby.bark() # WOOF
# METHOD OVERRIDE
boby.bark() # WoOoOoF!!
Gibt es in Python eine Möglichkeit, eine Klassenmethode auf Instanzebene zu überschreiben? Beispielsweise:
class Dog:
def bark(self):
print "WOOF"
boby = Dog()
boby.bark() # WOOF
# METHOD OVERRIDE
boby.bark() # WoOoOoF!!
Antworten:
Bitte tun Sie dies nicht wie gezeigt. Ihr Code wird unlesbar, wenn Sie eine Instanz monkeypatchen, um sich von der Klasse zu unterscheiden.
Sie können monkeypatched Code nicht debuggen.
Wenn Sie einen Fehler in bobyund findenprint type(boby) , werden Sie sehen, dass (a) es ein Hund ist, aber (b) aus einem unbekannten Grund nicht richtig bellt. Das ist ein Albtraum. TU es nicht.
Bitte machen Sie dies stattdessen.
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!!
Ja es ist möglich:
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"
funcTypeund warum dies erforderlich ist.
funcType = types.MethodType(nach dem Importieren types) anstelle von verwendet wird funcType = type(Dog.bark).
__get__die Funktion auch aufrufen , um sie an die Instanz zu binden.
Sie müssen MethodType aus dem Typmodul verwenden. Zweck von MethodType ist das Überschreiben von Methoden auf Instanzebene (damit self in überschriebenen Methoden verfügbar ist).
siehe unten Beispiel.
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!!
Um die hervorragende Antwort von @ codelogic zu erklären, schlage ich einen expliziteren Ansatz vor. Dies ist die gleiche Technik wie die. Operator eine Klassenmethode gründlich bindet, wenn Sie als Instanzattribut darauf zugreifen, mit der Ausnahme, dass Ihre Methode tatsächlich eine außerhalb einer Klasse definierte Funktion ist.
Bei der Arbeit mit dem Code von @ codelogic besteht der einzige Unterschied darin, wie die Methode gebunden ist. Ich verwende die Tatsache , dass die Funktionen und Methoden sind nicht-Daten - Deskriptoren in Python, und das Aufrufen von __get__Verfahren. Beachten Sie insbesondere, dass sowohl das Original als auch die Ersetzung identische Signaturen haben. Dies bedeutet, dass Sie die Ersetzung als vollständige Klassenmethode schreiben und über auf alle Instanzattribute zugreifen könnenself .
Klasse Hund:
def bark (self):
print "Woof"
def new_bark (self):
print "Woof Woof"
foo = Hund ()
# "Woof"
foo.bark ()
# Ersetzen Sie die Rinde nur für dieses Objekt durch new_bark
foo.bark = new_bark .__ get __ (foo, Hund)
foo.bark ()
# "Wau-Wau"
Durch Zuweisen der gebundenen Methode zu einem Instanzattribut haben Sie eine nahezu vollständige Simulation zum Überschreiben einer Methode erstellt. Eine praktische Funktion, die fehlt, ist der Zugriff auf die No-Arg-Version von super, da Sie sich nicht in einer Klassendefinition befinden. Eine andere Sache ist, dass das __name__Attribut Ihrer gebundenen Methode nicht den Namen der Funktion annimmt, die es überschreibt, wie es in der Klassendefinition der Fall wäre, aber Sie können es trotzdem manuell festlegen. Der dritte Unterschied besteht darin, dass Ihre manuell gebundene Methode eine einfache Attributreferenz ist, die zufällig eine Funktion ist. Der .Operator ruft lediglich diese Referenz ab. Beim Aufrufen einer regulären Methode von einer Instanz aus erstellt der Bindungsprozess jedes Mal eine neue gebundene Methode.
Der einzige Grund, warum dies funktioniert, ist übrigens, dass Instanzattribute Nicht-Daten- Deskriptoren überschreiben . Datendeskriptoren haben __set__Methoden, die Methoden (zum Glück für Sie) nicht haben. Datendeskriptoren in der Klasse haben tatsächlich Vorrang vor allen Instanzattributen. Aus diesem Grund können Sie einer Eigenschaft zuweisen: Ihre __set__Methode wird aufgerufen, wenn Sie versuchen, eine Zuweisung vorzunehmen. Ich persönlich gehe gerne noch einen Schritt weiter und verstecke den tatsächlichen Wert des zugrunde liegenden Attributs in der Instanz __dict__, auf die mit normalen Mitteln nicht zugegriffen werden kann, gerade weil die Eigenschaft es beschattet.
Sie sollten auch bedenken, dass dies für magische Methoden (doppelter Unterstrich) sinnlos ist . Magische Methoden können natürlich auf diese Weise überschrieben werden, aber die Operationen, die sie verwenden, betrachten nur den Typ. Sie können beispielsweise __contains__in Ihrer Instanz etwas Besonderes festlegen , aber ein Aufruf x in instancewürde dies ignorieren und type(instance).__contains__(instance, x)stattdessen verwenden. Dies gilt für alle im Python- Datenmodell angegebenen magischen Methoden .
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!!
Sie können die bobyVariable innerhalb der Funktion verwenden, wenn Sie dies benötigen. Da Sie die Methode nur für dieses eine Instanzobjekt überschreiben, ist dieser Weg einfacher und hat genau den gleichen Effekt wie die Verwendung self.
patchingund dies ist der richtige Weg, dies zu tun (zB boby = Dog()und boby.bark = new_bark). Es ist unglaublich nützlich beim Testen von Einheiten für Kontrollen. Weitere Erklärungen finden Sie unter tryolabs.com/blog/2013/07/05/run-time-method-patching-python (Beispiele) - nein, ich bin nicht mit der verlinkten Site oder dem verlinkten Autor verbunden.
Da hier niemand erwähnt functools.partial:
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!!
Da Funktionen in Python erstklassige Objekte sind, können Sie sie beim Initialisieren Ihres Klassenobjekts übergeben oder jederzeit für eine bestimmte Klasseninstanz überschreiben:
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()
und die Ergebnisse sind
calling original bark
woof
calling the new bark
wooOOOoof
calling another new
nowoof
Obwohl mir die Vererbungsidee von S. Lott gefallen hat und ich der Sache 'Typ (a)' zustimme, denke ich, dass Funktionen auf folgende Weise verwaltet werden können:
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__
und die Ausgabe ist:
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
Sehr geehrte Damen und Herren, dies wird nicht überschrieben. Sie rufen dieselbe Funktion nur zweimal mit dem Objekt auf. Grundsätzlich bezieht sich das Überschreiben auf mehr als eine Klasse. Wenn dieselbe Signaturmethode in verschiedenen Klassen vorhanden ist, entscheidet die Funktion, die Sie aufrufen, über das Objekt, das dies aufruft. Das Überschreiben ist in Python möglich, wenn Sie dafür sorgen, dass mehr als eine Klasse dieselben Funktionen schreibt und eine Sache mehr teilt, dass das Überladen in Python nicht zulässig ist
Ich fand, dass dies die genaueste Antwort auf die ursprüngliche Frage ist
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()