包装其方法返回该类实例的类

2024-05-17 16:27:16 发布

您现在位置:Python中文网/ 问答频道 /正文

我需要从第三方包装类。通常,第三方类具有返回第三方类实例的方法。这些方法的包装版本必须将这些实例转换为包装类的实例,但我无法使其工作。我将Python2.7与新样式的类一起使用。在

基于Create a wrapper class to call a pre and post function around existing functions?,我得到了以下内容。在

import copy

class Wrapper(object):
    __wraps__  = None

    def __init__(self, obj):
        if self.__wraps__ is None:
            raise TypeError("base class Wrapper may not be instantiated")
        elif isinstance(obj, self.__wraps__):
            self._obj = obj
        else:
            raise ValueError("wrapped object must be of %s" % self.__wraps__)

    def __getattr__(self, name):
        orig_attr = self._obj.__getattribute__(name)
        if callable(orig_attr):
            def hooked(*args, **kwargs):
                result = orig_attr(*args, **kwargs)
                if result == self._obj:
                    return result
                return self.__class__(result)
            return hooked
        else:
            return orig_attr

class ClassToWrap(object):
    def __init__(self, data):
        self.data = data

    def theirfun(self):
        new_obj = copy.deepcopy(self)
        new_obj.data += 1
        return new_obj

class Wrapped(Wrapper):
    __wraps__ = ClassToWrap

    def myfun(self):
        new_obj = copy.deepcopy(self)
        new_obj.data += 1
        return new_obj

obj = ClassToWrap(0)
wr0 = Wrapped(obj)
print wr0.data
>> 0
wr1 = wr0.theirfun()
print wr1.data
>> 1
wr2 = wr1.myfun()
print wr2.data
>> 2
wr3 = wr2.theirfun()
print wr3.data
>> 2

那么,为什么theirfun()第一次起作用,第二次却不行?wr0wr2都是Wrapped类型,调用wr2.theirfun()不会引发错误,但不会像预期的那样向wr2.data添加1。在

抱歉,但我不是在寻找以下替代方法:

  1. 猴子修补。我的代码库是非常重要的,我不知道如何确保补丁能够在import语句的web中传播。在
  2. 为每个第三方包编写所有这些复杂方法的单独包装方法。他们太多了。在

ETA:有几个有用的答案引用了Wrapper类之外的底层_obj属性。但是,这种方法的重点是可扩展性,因此这个功能需要在Wrapper类中。myfun需要按预期运行,而不在其定义中引用{}。在


Tags: 方法selfobjnewdatareturndefresult
2条回答

问题在于myfun中的赋值new_obj.data += 1。它的问题是new_objWrapped的实例,而不是{}的实例。您的Wrapper基类只支持在代理对象上查找属性。它不支持属性赋值。增广赋值同时做到了这两个方面,所以它不能完全正确地工作。在

您可以通过稍微改变一下myfun使其工作:

def myfun(self):
    new_obj = copy.deepcopy(self._obj)
    new_obj.data += 1
    return self.__class__(new_obj) # alternative spelling: return type(self)(new_obj)

解决这个问题的另一种方法是在Wrapper中添加一个__setattr__方法,但是让它正常工作(不干涉代理类自身的属性)会有点尴尬。在

与您当前的问题无关,您还可能泄漏hooked包装函数中的包装对象。如果您调用的方法返回被调用的对象(例如,方法did return self),那么您当前返回的对象是unwrapped。您可能需要将return result更改为return self,以便返回当前包装器。您可能还需要检查返回值,看看它是否是一个可以包装的类型。当前,如果一个方法返回字符串或数字或其他任何内容而不是包装类型的实例,则代码将失败。在

^{pr2}$

问题在于您在Wrapped类中实现myfun。您只更新了类instance的data成员,但是包装类(ClassToWrap实例,即_objdata成员是过时的,使用的是theirfun的前一个调用。在

您需要在两个实例之间同步数据值:

class Wrapper(object):
    ...
    def __setattr__(self, attr, val):
        object.__setattr__(self, attr, val)
        if getattr(self._obj, attr, self._obj) is not self._obj: # update _obj's member if it exists
            setattr(self._obj, attr, getattr(self, attr))


class Wrapped(Wrapper):
    ...
    def myfun(self):
        new_obj = copy.deepcopy(self)
        new_obj.data += 1
        return new_obj

obj = ClassToWrap(0)
wr0 = Wrapped(obj)
print wr0.data
# 0
wr1 = wr0.theirfun()
print wr1.data
# 1
wr2 = wr1.myfun()
print wr2.data
# 2
wr3 = wr2.theirfun()
print wr3.data
# 3
wr4 = wr3.myfun()
print wr4.data
# 4
wr5 = wr4.theirfun()
print wr5.data
# 5

相关问题 更多 >