C'è un modo in Python per sovrascrivere un metodo di classe a livello di istanza? Per esempio:
class Dog:
def bark(self):
print "WOOF"
boby = Dog()
boby.bark() # WOOF
# METHOD OVERRIDE
boby.bark() # WoOoOoF!!
C'è un modo in Python per sovrascrivere un metodo di classe a livello di istanza? Per esempio:
class Dog:
def bark(self):
print "WOOF"
boby = Dog()
boby.bark() # WOOF
# METHOD OVERRIDE
boby.bark() # WoOoOoF!!
Risposte:
Si prega di non farlo come mostrato. Il codice diventa illeggibile quando si esegue il monkeypatch di un'istanza in modo che sia diversa dalla classe.
Non è possibile eseguire il debug del codice con patch di scimmia.
Quando trovi un insetto in bobye print type(boby), vedrai che (a) è un cane, ma (b) per qualche oscuro motivo non abbaia correttamente. Questo è un incubo. Non farlo.
Per favore, fallo invece.
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!!
Si è possibile:
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"
funcTypefa e perché è necessario.
funcType = types.MethodType(dopo l'importazione types) invece di funcType = type(Dog.bark).
__get__la funzione per associarla all'istanza.
È necessario utilizzare MethodType dal typesmodulo. Lo scopo di MethodTypeè quello di sovrascrivere i metodi a livello di istanza (in modo che selfpossano essere disponibili nei metodi sovrascritti).
Vedi l'esempio sotto.
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!!
Per spiegare l'eccellente risposta di @ codelogic, propongo un approccio più esplicito. Questa è la stessa tecnica utilizzata .dall'operatore per associare un metodo di classe quando vi si accede come attributo di istanza, tranne per il fatto che il metodo sarà effettivamente una funzione definita al di fuori di una classe.
Lavorando con il codice di @ codelogic, l'unica differenza è nel modo in cui il metodo è associato. Sto usando il fatto che funzioni e metodi non sono descrittori di dati in Python e invoco il __get__metodo. Nota in particolare che sia l'originale che la sostituzione hanno firme identiche, il che significa che puoi scrivere la sostituzione come un metodo di classe completo, accedendo a tutti gli attributi dell'istanza tramite self.
classe Cane:
def bark (self):
stampa "Woof"
def new_bark (self):
stampa "Woof Woof"
foo = Cane ()
# "Woof"
foo.bark ()
# sostituisci bark con new_bark solo per questo oggetto
foo.bark = new_bark .__ get __ (foo, Dog)
foo.bark ()
# "Bau bau"
Assegnando il metodo vincolato a un attributo di istanza, hai creato una simulazione quasi completa dell'override di un metodo. Una caratteristica utile che manca è l'accesso alla versione senza argomenti di super, poiché non sei in una definizione di classe. Un'altra cosa è che l' __name__attributo del tuo metodo associato non prenderà il nome della funzione che sta sovrascrivendo, come nella definizione di classe, ma puoi comunque impostarlo manualmente. La terza differenza è che il tuo metodo associato manualmente è un semplice riferimento di attributo che sembra essere una funzione. L' .operatore non fa altro che recuperare quel riferimento. Quando si richiama un metodo regolare da un'istanza d'altra parte, il processo di associazione crea ogni volta un nuovo metodo associato.
L'unico motivo per cui funziona, a proposito, è che gli attributi di istanza sovrascrivono i descrittori non di dati . I descrittori di dati hanno __set__metodi, che metodi (fortunatamente per te) no. I descrittori di dati nella classe hanno effettivamente la priorità su qualsiasi attributo di istanza. Questo è il motivo per cui puoi assegnare una proprietà: il loro __set__metodo viene richiamato quando provi a fare un'assegnazione. Personalmente mi piace fare un ulteriore passo avanti e nascondere il valore effettivo dell'attributo sottostante nell'istanza __dict__, dove è inaccessibile con mezzi normali proprio perché la proprietà lo ombreggia.
Dovresti anche tenere a mente che questo è inutile per i metodi magici (doppio trattino basso) . I metodi magici possono ovviamente essere sovrascritti in questo modo, ma le operazioni che li usano guardano solo al tipo. Ad esempio, puoi impostare __contains__qualcosa di speciale nella tua istanza, ma la chiamata x in instancelo ignorerebbe e userebbe type(instance).__contains__(instance, x)invece. Questo si applica a tutti i metodi magici specificati nel modello di dati Python .
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!!
È possibile utilizzare la bobyvariabile all'interno della funzione, se necessario. Poiché stai sovrascrivendo il metodo solo per questo oggetto istanza, in questo modo è più semplice e ha esattamente lo stesso effetto dell'uso self.
patchinge questo è il modo corretto per farlo (ad esempio boby = Dog()e boby.bark = new_bark). È incredibilmente utile nei test unitari per i controlli. Per ulteriori spiegazioni vedere tryolabs.com/blog/2013/07/05/run-time-method-patching-python (esempi) - no non sono affiliato con il sito collegato o l'autore.
Poiché nessuno sta menzionando functools.partialqui:
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!!
Poiché le funzioni sono oggetti di prima classe in Python, puoi passarle durante l'inizializzazione dell'oggetto classe o sovrascriverlo in qualsiasi momento per una determinata istanza di classe:
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()
e i risultati sono
calling original bark
woof
calling the new bark
wooOOOoof
calling another new
nowoof
Anche se mi è piaciuta l'idea di ereditarietà di S. Lott e sono d'accordo con la cosa 'tipo (a)', ma poiché anche le funzioni hanno attributi accessibili, penso che possa essere gestita in questo modo:
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__
e l'output è:
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
Caro, questo non sta sovrascrivendo, stai solo chiamando la stessa funzione due volte con l'oggetto. Fondamentalmente l'override è correlato a più di una classe. quando lo stesso metodo di firma esiste in classi diverse, la funzione che stai chiamando decide l'oggetto che la chiama. L'override è possibile in python quando si crea più di una classe, vengono scritte le stesse funzioni e una cosa in più da condividere che il sovraccarico non è consentito in python
Ho trovato questa la risposta più precisa alla domanda originale
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()