Come posso semplicemente ereditare metodi da un'istanza esistente?
-
09-06-2019 - |
Domanda
Di seguito ho un esempio molto semplice di ciò che sto cercando di fare.Voglio essere in grado di utilizzare HTMLDecorator con qualsiasi altra classe.Ignora il fatto che si chiama decoratore, è solo un nome.
import cgi
class ClassX(object):
pass # ... with own __repr__
class ClassY(object):
pass # ... with own __repr__
inst_x=ClassX()
inst_y=ClassY()
inst_z=[ i*i for i in range(25) ]
inst_b=True
class HTMLDecorator(object):
def html(self): # an "enhanced" version of __repr__
return cgi.escape(self.__repr__()).join(("<H1>","</H1>"))
print HTMLDecorator(inst_x).html()
print HTMLDecorator(inst_y).html()
wrapped_z = HTMLDecorator(inst_z)
inst_z[0] += 70
wrapped_z[0] += 71
print wrapped_z.html()
print HTMLDecorator(inst_b).html()
Produzione:
Traceback (most recent call last): File "html.py", line 21, in print HTMLDecorator(inst_x).html() TypeError: default __new__ takes no parameters
Ciò che sto cercando di fare è possibile?Se sì, cosa sto facendo di sbagliato?
Soluzione
Molto vicino, ma poi perdo tutto da ClassX.Di seguito c'è qualcosa che mi ha dato un collega che funziona, ma è orribile.Ci deve essere un modo migliore.
Sembra che tu stia cercando di impostare una sorta di schema di oggetti proxy.È fattibile e ci sono soluzioni migliori di quelle del tuo collega, ma prima considera se sarebbe più semplice applicare semplicemente alcune patch in più.Questo non funzionerà per classi integrate come bool
, ma lo farà per le classi definite dall'utente:
def HTMLDecorator (obj):
def html ():
sep = cgi.escape (repr (obj))
return sep.join (("<H1>", "</H1>"))
obj.html = html
return obj
Ed ecco la versione proxy:
class HTMLDecorator(object):
def __init__ (self, wrapped):
self.__wrapped = wrapped
def html (self):
sep = cgi.escape (repr (self.__wrapped))
return sep.join (("<H1>", "</H1>"))
def __getattr__ (self, name):
return getattr (self.__wrapped, name)
def __setattr__ (self, name, value):
if not name.startswith ('_HTMLDecorator__'):
setattr (self.__wrapped, name, value)
return
super (HTMLDecorator, self).__setattr__ (name, value)
def __delattr__ (self, name):
delattr (self.__wraped, name)
Altri suggerimenti
Entrambe le soluzioni di John funzionerebbero.Un'altra opzione che consente a HTMLDecorator di rimanere molto semplice e pulito è quella di inserirlo come classe base.Funziona anche solo per le classi definite dall'utente, non per i tipi incorporati:
import cgi
class ClassX(object):
pass # ... with own __repr__
class ClassY(object):
pass # ... with own __repr__
inst_x=ClassX()
inst_y=ClassY()
class HTMLDecorator:
def html(self): # an "enhanced" version of __repr__
return cgi.escape(self.__repr__()).join(("<H1>","</H1>"))
ClassX.__bases__ += (HTMLDecorator,)
ClassY.__bases__ += (HTMLDecorator,)
print inst_x.html()
print inst_y.html()
Attenzione, però: patch di questo tipo hanno un prezzo elevato in termini di leggibilità e manutenibilità del codice.Quando torni a questo codice un anno dopo, può diventare molto difficile capire come il tuo ClassX abbia ottenuto quel metodo html(), specialmente se ClassX è definito in qualche altra libreria.
Ciò che sto cercando di fare è possibile?Se sì, cosa sto facendo di sbagliato?
È certamente possibile.Ciò che è sbagliato è questo HTMLDecorator.__init__()
non accetta parametri.
Ecco un semplice esempio:
def decorator (func):
def new_func ():
return "new_func %s" % func ()
return new_func
@decorator
def a ():
return "a"
def b ():
return "b"
print a() # new_func a
print decorator (b)() # new_func b
@Giovanni (37448):
Scusa, potrei averti ingannato con il nome (pessima scelta).Non sto davvero cercando una funzione di decoratore, o qualcosa che abbia a che fare con i decoratori.Quello che cerco è che html(self) def utilizzi ClassX o ClassY __repr__
.Voglio che funzioni senza modificare ClassX o ClassY.
Ah, in tal caso, forse un codice come questo sarà utile?In realtà non ha nulla a che fare con i decoratori, ma mostra come passare argomenti alla funzione di inizializzazione di una classe e come recuperarli per dopo.
import cgi
class ClassX(object):
def __repr__ (self):
return "<class X>"
class HTMLDecorator(object):
def __init__ (self, wrapped):
self.__wrapped = wrapped
def html (self):
sep = cgi.escape (repr (self.__wrapped))
return sep.join (("<H1>", "</H1>"))
inst_x=ClassX()
inst_b=True
print HTMLDecorator(inst_x).html()
print HTMLDecorator(inst_b).html()
@Giovanni (37479):
Molto vicino, ma poi perdo tutto da ClassX.Di seguito c'è qualcosa che mi ha dato un collega che funziona, ma è orribile.Ci deve essere un modo migliore.
import cgi
from math import sqrt
class ClassX(object):
def __repr__(self):
return "Best Guess"
class ClassY(object):
pass # ... with own __repr__
inst_x=ClassX()
inst_y=ClassY()
inst_z=[ i*i for i in range(25) ]
inst_b=True
avoid="__class__ __init__ __dict__ __weakref__"
class HTMLDecorator(object):
def __init__(self,master):
self.master = master
for attr in dir(self.master):
if ( not attr.startswith("__") or
attr not in avoid.split() and "attr" not in attr):
self.__setattr__(attr, self.master.__getattribute__(attr))
def html(self): # an "enhanced" version of __repr__
return cgi.escape(self.__repr__()).join(("<H1>","</H1>"))
def length(self):
return sqrt(sum(self.__iter__()))
print HTMLDecorator(inst_x).html()
print HTMLDecorator(inst_y).html()
wrapped_z = HTMLDecorator(inst_z)
print wrapped_z.length()
inst_z[0] += 70
#wrapped_z[0] += 71
wrapped_z.__setitem__(0,wrapped_z.__getitem__(0)+ 71)
print wrapped_z.html()
print HTMLDecorator(inst_b).html()
Produzione:
<H1>Best Guess</H1> <H1><__main__.ClassY object at 0x891df0c></H1> 70.0 <H1>[141, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361, 400, 441, 484, 529, 576]</H1> <H1>True</H1>