用Python完全包装一个对象

时间:2009-09-18 08:01:43

标签: python reflection

我想完全包装一个对象,以便将所有属性和方法请求转发到它包装的对象,还要覆盖我想要的任何方法或变量,以及提供我自己的一些方法。这个包装类应该100%出现,因为现有的类(isinstance必须表现得好像它实际上是类),但是子类化本身不会削减它,因为我想包装一个现有的对象。 Python中是否有一些解决方案可以做到这一点?我正在思考以下几点:

class ObjectWrapper(BaseClass):
    def __init__(self, baseObject):
        self.baseObject = baseObject

    def overriddenMethod(self):
        ...

    def myOwnMethod1(self):
        ...

    ...

    def __getattr__(self, attr):
        if attr in ['overriddenMethod', 'myOwnMethod1', 'myOwnMethod2', ...]
            # return the requested method
        else:
            return getattr(self.baseObject, attr)

但我对覆盖__getattr____setattr____hasattr__并不熟悉,所以我不确定如何做到这一点。

6 个答案:

答案 0 :(得分:34)

在大多数情况下,最简单的方法可能是:

class ObjectWrapper(BaseClass):
    def __init__(self, baseObject):
        self.__class__ = type(baseObject.__class__.__name__,
                              (self.__class__, baseObject.__class__),
                              {})
        self.__dict__ = baseObject.__dict__

    def overriddenMethod(self):
        ...

以这种方式工作,即通过以这种方式重新分配自己的__class____dict__,你只需提供你的覆盖 - Python的正常属性获取和设置机制将完成其余的工作...... < EM>大多

只有在baseObject.__class__定义__slots__时才会遇到麻烦,在这种情况下,多重继承方法不起作用,你需要繁琐的__getattr__(正如其他人所说,至少你不必担心它会被你所覆盖的属性调用,因为它不会! - ),__setattr__(更大的痛苦,因为每个属性都会被调用),等等;并且使isinstance和特殊方法工作需要艰苦而繁琐的详细工作。

基本上,__slots__意味着一个类是特殊的,每个实例都是一个轻量级的“值对象”,不需要进行进一步复杂的操​​作,包装等,因为需要为每个实例保存几个字节该类超越了所有关于灵活性的正常关注等等;因此,以同样流畅和灵活的方式处理这些极端罕见的类,因为你可以处理99%以上的Python对象,这并不奇怪,这真的很痛苦。那么你需要处理__slots__(为了那些极端情况而编写,测试,调试和维护数百行代码),还是只有6行的99%解决方案就足够了? - )

还应该注意,这可能会导致内存泄漏,因为创建子类会将子类添加到基类的子类列表中,并且在子类的所有实例都是GC时不会被删除。

答案 1 :(得分:4)

__getattr__的优点是只在属性不存在时调用它,因此您不需要显式列表 - 您未定义的任何内容都将自动获得代理。

__setattr__比较棘手,因为它总是被调用。确保在设置自己的属性时使用超类调用或object.__setattr__;在setattr()中使用__setattr__将导致无限递归。

影响实例的最后一点非常困难。您可以使用对包装器实例的。__class__变量的赋值(但这也会覆盖类字典解析顺序),或者使用元类动态构造包装类型。由于isinstance在Python代码中是如此罕见,实际上试图欺骗它似乎有点过分了。

More information on special attribute access methods.

Even more information on them, plus some help with metaclasses.

答案 2 :(得分:4)

请查看http://code.activestate.com/recipes/577555-object-wrapper-class/以获取完整代码,包括重要评论。归结为:

class Wrapper(object):
    def __init__(self, obj):
        self._wrapped_obj = obj
    def __getattr__(self, attr):
        if attr in self.__dict__:
            return getattr(self, attr)
        return getattr(self._wrapped_obj, attr)

答案 3 :(得分:1)

注意:这个答案很老,可能不适用于现代蟒蛇。我相信它曾经用于2.6

从这开始,并在循环中弄乱东西以满足您的需求:

import inspect
class Delegate(object):
    def __init__(self, implementation):
        self.__class__ = implementation.__class__
        for n, m in inspect.getmembers(implementation, callable):
            if not n.startswith('_'):
                setattr(self, n, m)

包装非新风格对象的能力留给读者练习:)

答案 4 :(得分:0)

“现有对象”是指另一个类的实例?听起来好像你只需要继承基类。创建新对象时,传入基础对象的详细信息,或在新类中添加一个方法,该方法将基类实例的数据复制到自身。

答案 5 :(得分:-1)

以下方法与the one by Alex Martelli类似,但对我来说更具可读性:

我没有创建新类,而是创建了一个类实现的函数,它实例化底层对象并覆盖所需的函数,如下所示:

class C1:
    def __init__(self, p):
        self.p = p
    def f(self):
        print("This is C1, p=%s" % self.p)

def C2(p):
    c = C1(p)
    def new_f():
        print("This is C2, p is %s" % c.p)
    c.f = new_f
    def g(param):
        print("This is C2.g(%s), p is %s" % (param, c.p))
    c.g = g
    return c

c1 = C1('abc')
c1.f()

c2 = C2('cde')
c2.f()
c2.g('xyz')

输出:

This is C1, p=abc

This is C2, p is cde
This is C2.g(xyz), p is cde