如何简单地从现有实例继承方法?

1 投票
6 回答
538 浏览
提问于 2025-04-10 23:58

下面我有一个非常简单的例子,想给大家展示我想做的事情。我希望能够把HTMLDecorator和其他任何类一起使用。别在意它叫做decorator,这只是个名字。

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

输出结果:

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

我想做的事情可能吗?如果可以的话,我哪里做错了呢?

6 个回答

0

我想做的事情可能吗?如果可以,那我哪里做错了?

当然是可能的。你出错的地方在于 HTMLDecorator.__init__() 这个函数不接受参数。

下面是一个简单的例子:

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
2

约翰的两个解决方案都可以用。还有一个选择是把HTMLDecorator作为一个基础类来“猴子补丁”,这样可以让它保持简单和干净。不过,这种方法只适用于用户自己定义的类,而不适用于内置类型:

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

不过要注意,像这样进行猴子补丁会让你的代码可读性和可维护性大大降低。当你一年后再回来看这段代码时,可能会很难搞清楚你的ClassX是怎么得到这个html()方法的,尤其是当ClassX是在其他库中定义的时候。

2

你做得很接近,但我失去了ClassX里的所有内容。下面是我同事给我的一个方法,虽然能解决问题,但看起来很糟糕。肯定还有更好的办法。

看起来你想要设置一种代理对象的方案。这是可以做到的,而且比你同事的方法有更好的解决方案。不过,首先要考虑一下,直接添加一些额外的方法是不是更简单。对于像bool这样的内置类,这种方法不适用,但对于你自己定义的类是可以的:

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

这是代理版本的代码:

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)

撰写回答