Hoe kan ek metodes van 'n bestaande geval erf eenvoudig?
-
09-06-2019 - |
Vra
Hier het ek 'n baie eenvoudige voorbeeld van wat ek probeer om te doen. Ek wil in staat wees om HTMLDecorator gebruik met enige ander klas. Ignoreer die feit dit is bekend as versierder, dit is net 'n naam.
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()
Uitgawe:
Traceback (most recent call last): File "html.py", line 21, in print HTMLDecorator(inst_x).html() TypeError: default __new__ takes no parameters
Is dit wat ek probeer om moontlike doen? So ja, wat doen ek verkeerd?
Oplossing
Baie naby, maar dan alles verloor ek uit ClassX. Hieronder is iets wat 'n kollega het my vertel dat doen doen die truuk, maar dit is afskuwelik. Daar moet 'n beter manier wees.
Dit lyk asof jy probeer om op te rig 'n soort van 'n gevolmagtigde voorwerp skema. Dit is uitvoerbaar, en daar is 'n beter oplossing as jou kollega se, maar eers oorweeg of dit makliker sou wees om net te lap in 'n paar ekstra metodes. Dit sal nie werk vir ingeboude klasse soos bool
, maar dit sal vir jou gebruiker-gedefinieerde klasse:
def HTMLDecorator (obj):
def html ():
sep = cgi.escape (repr (obj))
return sep.join (("<H1>", "</H1>"))
obj.html = html
return obj
En hier is die volmag weergawe:
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)
Ander wenke
Beide van John se oplossings sal werk. Nog 'n opsie wat jou toelaat HTMLDecorator om baie eenvoudig en skoon bly is om aap-kol dit in as 'n basis klas. Dit werk ook net vir die gebruiker-gedefinieerde klasse, nie ingeboude tipes:
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()
Wees gewaarsku, maar - aap-lap soos hierdie kom met 'n hoë prys in leesbaarheid en instandhouding van jou kode. Wanneer jy 'n jaar later terug te gaan na hierdie kode, kan dit baie moeilik geword om uit te vind hoe jou ClassX het dat html metode (), veral as ClassX word gedefinieer in 'n ander biblioteek.
Is dit wat ek probeer om moontlike doen? So ja, wat doen ek verkeerd?
Dit is seker moontlik. Wat is fout is dat HTMLDecorator.__init__()
nie parameters aanvaar nie.
Hier is 'n eenvoudige voorbeeld:
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):
Jammer, ek kan julle mislei met die naam (slegte keuse). Ek is nie regtig op soek na 'n versierder funksie, of niks te doen met ontwerpers at all. Wat ek is nadat is vir die html (self) def om ClassX of classy se __repr__
gebruik. Ek wil hierdie om te werk sonder wysiging ClassX of classy.
Ag, in daardie geval, miskien kode soos hierdie sal nuttig wees? Dit maak nie regtig iets te doen met ontwerpers, maar toon hoe om argumente te slaag om inisialisering funksie 'n klas en aan dié argumente te haal vir later.
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):
Baie naby, maar dan alles verloor ek uit ClassX. Hieronder is iets wat 'n kollega het my vertel dat doen doen die truuk, maar dit is afskuwelik. Daar moet 'n beter manier wees.
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()
Uitgawe:
<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>