Pergunta

Abaixo tenho um exemplo bem simples do que estou tentando fazer.Quero poder usar HTMLDecorator com qualquer outra classe.Ignore o fato de se chamar decorador, é apenas um 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()

Saída:

Traceback (most recent call last):
  File "html.py", line 21, in 
    print HTMLDecorator(inst_x).html()
TypeError: default __new__ takes no parameters

O que estou tentando fazer é possível?Se sim, o que estou fazendo de errado?

Foi útil?

Solução

Muito perto, mas depois perco tudo do ClassX.Abaixo está algo que um colega me deu que funciona, mas é horrível.Tem que haver uma maneira melhor.

Parece que você está tentando configurar algum tipo de esquema de objeto proxy.Isso é possível e existem soluções melhores que as do seu colega, mas primeiro considere se seria mais fácil apenas corrigir alguns métodos extras.Isso não funcionará para classes internas como bool, mas será para suas classes definidas pelo usuário:

def HTMLDecorator (obj):
    def html ():
        sep = cgi.escape (repr (obj))
        return sep.join (("<H1>", "</H1>"))
    obj.html = html
    return obj

E aqui está a versão do 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)

Outras dicas

Ambas as soluções de John funcionariam.Outra opção que permite que o HTMLDecorator permaneça muito simples e limpo é aplicá-lo como uma classe base.Isso também funciona apenas para classes definidas pelo usuário, não para tipos internos:

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()

Porém, esteja avisado - patches de macaco como esse têm um alto preço em termos de legibilidade e manutenção do seu código.Quando você voltar a esse código um ano depois, pode se tornar muito difícil descobrir como seu ClassX obteve esse método html(), especialmente se ClassX estiver definido em alguma outra biblioteca.

O que estou tentando fazer é possível?Se sim, o que estou fazendo de errado?

Certamente é possível.O que há de errado é que HTMLDecorator.__init__() não aceita parâmetros.

Aqui está um exemplo simples:

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):

Desculpe, posso ter enganado você com o nome (má escolha).Na verdade, não estou procurando uma função de decorador, nem nada relacionado a decoradores.O que procuro é que o html(self) def use ClassX ou ClassY's __repr__.Quero que isso funcione sem modificar ClassX ou ClassY.

Ah, nesse caso, talvez um código como este seja útil?Na verdade, não tem nada a ver com decoradores, mas demonstra como passar argumentos para a função de inicialização de uma classe e recuperar esses argumentos para uso posterior.

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()

@João (37479):

Muito perto, mas depois perco tudo do ClassX.Abaixo está algo que um colega me deu que funciona, mas é horrível.Tem que haver uma maneira melhor.

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()

Saída:

<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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top