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?

È stato utile?

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>
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top