¿Cómo puedo simplemente heredar métodos de una instancia existente?
-
09-06-2019 - |
Pregunta
A continuación tengo un ejemplo muy simple de lo que estoy tratando de hacer.Quiero poder usar HTMLDecorator con cualquier otra clase.Ignora el hecho de que se llama decorador, es sólo un nombre.
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()
Producción:
Traceback (most recent call last): File "html.py", line 21, in print HTMLDecorator(inst_x).html() TypeError: default __new__ takes no parameters
¿Es posible lo que estoy intentando hacer?Si es así, ¿qué estoy haciendo mal?
Solución
Muy cerca, pero luego pierdo todo de ClassX.A continuación se muestra algo que me dio un colega y que funciona, pero es horrible.Tiene que haber una mejor manera.
Parece que estás intentando configurar algún tipo de esquema de objetos proxy.Eso es factible y existen mejores soluciones que las de su colega, pero primero considere si sería más fácil simplemente implementar algunos métodos adicionales.Esto no funcionará para clases integradas como bool
, pero lo será para sus clases definidas por el usuario:
def HTMLDecorator (obj):
def html ():
sep = cgi.escape (repr (obj))
return sep.join (("<H1>", "</H1>"))
obj.html = html
return obj
Y aquí está la versión 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)
Otros consejos
Ambas soluciones de John funcionarían.Otra opción que permite que HTMLDecorator siga siendo muy simple y limpio es parchearlo como clase base.Esto también funciona sólo para clases definidas por el usuario, no para tipos integrados:
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()
Sin embargo, tenga cuidado: aplicar parches como este tiene un alto precio en legibilidad y mantenibilidad de su código.Cuando vuelves a este código un año después, puede resultar muy difícil descubrir cómo tu ClassX obtuvo ese método html(), especialmente si ClassX está definido en alguna otra biblioteca.
¿Es posible lo que estoy intentando hacer?Si es así, ¿qué estoy haciendo mal?
Ciertamente es posible.Lo que pasa es que HTMLDecorator.__init__()
No acepta parámetros.
He aquí un ejemplo sencillo:
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
@Juan (37448):
Lo siento, es posible que te haya engañado con el nombre (mala elección).Realmente no estoy buscando una función de decorador, ni nada que tenga que ver con decoradores.Lo que busco es que html(self)def use ClassX o ClassY's __repr__
.Quiero que esto funcione sin modificar ClassX o ClassY.
Ah, en ese caso, ¿quizás un código como este sea útil?Realmente no tiene nada que ver con decoradores, pero demuestra cómo pasar argumentos a la función de inicialización de una clase y recuperar esos argumentos para más adelante.
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()
@Juan (37479):
Muy cerca, pero luego pierdo todo de ClassX.A continuación se muestra algo que me dio un colega y que funciona, pero es horrible.Tiene que haber una mejor manera.
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()
Producción:
<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>