Wie kann ich Methoden einfach von einer vorhandenen Instanz erben?
-
09-06-2019 - |
Frage
Unten habe ich ein sehr einfaches Beispiel dafür, was ich versuche.Ich möchte HTMLDecorator mit jeder anderen Klasse verwenden können.Ignorieren Sie die Tatsache, dass es Decorator heißt, es ist nur ein Name.
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()
Ausgabe:
Traceback (most recent call last): File "html.py", line 21, in print HTMLDecorator(inst_x).html() TypeError: default __new__ takes no parameters
Ist das, was ich versuche, möglich?Wenn ja, was mache ich falsch?
Lösung
Sehr knapp, aber dann verliere ich alles von ClassX.Unten ist etwas, das mir ein Kollege gegeben hat und das zwar funktioniert, aber abscheulich ist.Es muss einen besseren Weg geben.
Sieht so aus, als würden Sie versuchen, eine Art Proxy-Objektschema einzurichten.Das ist machbar und es gibt bessere Lösungen als die Ihres Kollegen, aber überlegen Sie zunächst, ob es einfacher wäre, einfach ein paar zusätzliche Methoden einzubinden.Dies funktioniert nicht für integrierte Klassen wie bool
, aber für Ihre benutzerdefinierten Klassen gilt Folgendes:
def HTMLDecorator (obj):
def html ():
sep = cgi.escape (repr (obj))
return sep.join (("<H1>", "</H1>"))
obj.html = html
return obj
Und hier ist die Proxy-Version:
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)
Andere Tipps
Beide Lösungen von John würden funktionieren.Eine weitere Option, die es HTMLDecorator ermöglicht, sehr einfach und sauber zu bleiben, besteht darin, es als Basisklasse einzubinden.Dies funktioniert auch nur für benutzerdefinierte Klassen, nicht für integrierte Typen:
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()
Seien Sie jedoch gewarnt – ein derartiges Monkey-Patching geht mit einem hohen Preis für die Lesbarkeit und Wartbarkeit Ihres Codes einher.Wenn Sie ein Jahr später zu diesem Code zurückkehren, kann es sehr schwierig werden herauszufinden, wie Ihr ClassX zu dieser html()-Methode gelangt ist, insbesondere wenn ClassX in einer anderen Bibliothek definiert ist.
Ist das, was ich versuche, möglich?Wenn ja, was mache ich falsch?
Es ist sicherlich möglich.Was falsch ist, ist das HTMLDecorator.__init__()
akzeptiert keine Parameter.
Hier ist ein einfaches Beispiel:
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
@John (37448):
Entschuldigung, ich habe Sie möglicherweise mit dem Namen in die Irre geführt (schlechte Wahl).Ich bin nicht wirklich auf der Suche nach einer Funktion als Dekorateur oder irgendetwas, das überhaupt etwas mit Dekorateuren zu tun hat.Ich möchte, dass die HTML-Definition (selbst) ClassX oder ClassY verwendet __repr__
.Ich möchte, dass dies funktioniert, ohne ClassX oder ClassY zu ändern.
Ah, in diesem Fall ist Code wie dieser vielleicht nützlich?Es hat nicht wirklich etwas mit Dekoratoren zu tun, sondern zeigt, wie man Argumente an die Initialisierungsfunktion einer Klasse übergibt und diese Argumente für später abruft.
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()
@John (37479):
Sehr knapp, aber dann verliere ich alles von ClassX.Unten ist etwas, das mir ein Kollege gegeben hat und das zwar funktioniert, aber abscheulich ist.Es muss einen besseren Weg geben.
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()
Ausgabe:
<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>