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?

¿Fue útil?

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>
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top