7

I have many files using classes with the following syntax:

o = module.CreateObject()
a = o.get_Field

and now the implementation has changed from 'get_XXX' and 'set_XXX' to just 'XXX':

o = module.CreateObject()
a = o.Field

This implementation is an external package, which I don't want to change. Is it possible to write a wrapper which will on-the-fly intercept all calls to 'get_XXX' and replace then with calls to the new name 'XXX'?

o = MyRenamer(module.CreateObject())
a = o.get_Field   # works as before, o.Field is called
a = o.DoIt()      # works as before, o.DoIt is called

It needs to intercept all calls, not just to a finite-set of fields, decide based on the method name if to modify it and cause a method with a modified name to be called.

4

2 回答 2

4

如果您想继续使用get_Fieldset_Field在已切换到使用属性的对象上(您只需访问或分配给Field),则可以使用包装器对象:

class NoPropertyAdaptor(object):
    def __init__(self, obj):
        self.obj = obj

    def __getattr__(self, name):
        if name.startswith("get_"):
            return lambda: getattr(self.obj, name[4:])
        elif name.startswith("set_"):
            return lambda value: setattr(self.obj, name[4:], value)
        else:
            return getattr(self.obj, name)

如果您使用额外的语法,例如对对象进行索引或迭代,或者您需要使用isinstance.

一个更复杂的解决方案是创建一个子类来重写名称并强制对象使用它。这并不完全是一个包装,因为外部代码仍将直接处理对象(因此魔术方法和isinstance)将按预期工作。这种方法适用于大多数对象,但对于具有奇特元类魔法的类型和某些内置类型可能会失败:

def no_property_adaptor(obj):
    class wrapper(obj.__class__):
        def __getattr__(self, name):
            if name.startswith("get_"):
                return lambda: getattr(self, name[4:])
            elif name.startswith("set_"):
                return lambda value: setattr(self, name[4:], value)
            else:
                return super(wrapper, self).__getattr__(name)

    obj.__class__ = wrapper
    return obj
于 2013-04-24T06:37:35.237 回答
1

您可以“猴子补丁”任何 python 类;直接导入类并添加一个属性:

import original_module

@property
def get_Field(self):
    return self.Field

original_module.OriginalClass.get_Field = get_Field

您需要枚举您希望以这种方式访问​​的字段:

def addField(fieldname, class):
    @property
    def get_Field(self):
        return getattr(self, fieldname)

    setattr(original_module.OriginalClass, 'get_{}'.format(fieldname), get_Field)

for fieldname in ('Foo', 'Bar', 'Baz'):
    addField(fieldname, original_module.OriginalClass)
于 2013-04-24T06:34:52.537 回答