代码之家  ›  专栏  ›  技术社区  ›  Smashery

用Python完全包装对象

  •  29
  • Smashery  · 技术社区  · 15 年前

    我想完全包装一个对象,这样所有的属性和方法请求都会被转发到它包装的对象,但也会覆盖我想要的任何方法或变量,并提供一些我自己的方法。此包装类应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 回复  |  直到 15 年前
        1
  •  45
  •   David Wolever    8 年前

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

    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的常规属性获取和设置机制将完成其余的工作。。。 主要地 .

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

    基本上, __槽__ 意味着一个类是一个特殊的类,每个实例都是一个轻量级的“值对象”,不会受到进一步复杂的操作、包装等的影响,因为需要为该类的每个实例节省几个字节,这会覆盖所有关于灵活性等的正常关注;因此,以与处理99%以上Python对象相同的平滑灵活的方式处理如此极端、罕见的类确实是一件痛苦的事情,这并不奇怪。那么你需要处理吗 __槽__ (就编写、测试、调试和维护数百行代码而言,仅仅是为了这些极端情况),或者说,用六行代码就可以得到99%的解决方案吗?-)

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

        2
  •  10
  •   Phil Friesen    9 年前

    请看 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
  •  5
  •   Uli Köhler Emmanuel Caradec    9 年前

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

    __setattr__ 更棘手的是,它总是被称为。确保使用超类调用或 object.__setattr__ setattr() 在内部 __赛特特__ 将导致无限递归。

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

    More information on special attribute access methods.

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

        4
  •  1
  •   Henrik Gustafsson    6 年前

    注: 这个答案非常古老,可能对现代蟒蛇不起作用。我相信它曾经在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)
    

        5
  •  0
  •   Jon Cage    15 年前

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

        6
  •  -1
  •   Community Neeleshkumar S    7 年前

    以下方法类似于 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