简体   繁体   English

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

[英]How can I simply inherit methods from an existing instance?

Below I have a very simple example of what I'm trying to do.下面我有一个非常简单的例子来说明我正在尝试做的事情。 I want to be able to use HTMLDecorator with any other class.我希望能够将 HTMLDecorator 与任何其他类一起使用。 Ignore the fact it's called decorator, it's just a name.忽略它被称为装饰器的事实,它只是一个名字。

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

Output:输出:

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

Is what I'm trying to do possible?我正在尝试做的可能吗? If so, what am I doing wrong?如果是这样,我做错了什么?

Very close, but then I lose everything from ClassX.非常接近,但随后我失去了 ClassX 的一切。 Below is something a collegue gave me that does do the trick, but it's hideous.下面是一位同事给我的东西,确实可以解决问题,但它很可怕。 There has to be a better way.必须有更好的方法。

Looks like you're trying to set up some sort of proxy object scheme.看起来您正在尝试设置某种代理对象方案。 That's doable, and there are better solutions than your colleague's, but first consider whether it would be easier to just patch in some extra methods.这是可行的,并且有比您同事更好的解决方案,但首先要考虑是否只修补一些额外的方法会更容易。 This won't work for built-in classes like bool , but it will for your user-defined classes:这不适用于bool等内置类,但适用于您的用户定义类:

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

And here is the proxy version:这是代理版本:

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)

Both of John's solutions would work. John 的两种解决方案都有效。 Another option that allows HTMLDecorator to remain very simple and clean is to monkey-patch it in as a base class.允许 HTMLDecorator 保持非常简单和干净的另一个选项是将其作为基类进行猴子修补。 This also works only for user-defined classes, not builtin types:这也仅适用于用户定义的类,不适用于内置类型:

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

Be warned, though -- monkey-patching like this comes with a high price in readability and maintainability of your code.但是请注意——像这样的猴子补丁会在代码的可读性和可维护性方面付出高昂的代价。 When you go back to this code a year later, it can become very difficult to figure out how your ClassX got that html() method, especially if ClassX is defined in some other library.当您一年后回到这段代码时,可能会很难弄清楚您的 ClassX 是如何获得该 html() 方法的,尤其是如果 ClassX 是在其他某个库中定义的。

Is what I'm trying to do possible?我正在尝试做的可能吗? If so, what am I doing wrong?如果是这样,我做错了什么?

It's certainly possible.这当然是可能的。 What's wrong is that HTMLDecorator.__init__() doesn't accept parameters.问题是HTMLDecorator.__init__()不接受参数。

Here's a simple example:这是一个简单的例子:

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): @约翰(37448):

Sorry, I might have misled you with the name (bad choice).抱歉,我可能用名字误导了您(错误的选择)。 I'm not really looking for a decorator function, or anything to do with decorators at all.我并不是真的在寻找装饰器功能,或者根本没有与装饰器有关的东西。 What I'm after is for the html(self) def to use ClassX or ClassY's __repr__ .我所追求的是让 html(self) def 使用 ClassX 或 ClassY 的__repr__ I want this to work without modifying ClassX or ClassY.我希望它在不修改 ClassX 或 ClassY 的情况下工作。

Ah, in that case, perhaps code like this will be useful?啊,那样的话,也许这样的代码会有用? It doesn't really have anything to do with decorators, but demonstrates how to pass arguments to a class's initialization function and to retrieve those arguments for later.它实际上与装饰器没有任何关系,但演示了如何将参数传递给类的初始化函数并在以后检索这些参数。

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

@John (37479): @约翰(37479):

Very close, but then I lose everything from ClassX.非常接近,但随后我失去了 ClassX 的一切。 Below is something a collegue gave me that does do the trick, but it's hideous.下面是一位同事给我的东西,确实可以解决问题,但它很可怕。 There has to be a better way.必须有更好的方法。

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

Output:输出:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM