Python:即时重命名方法名称

时间:2013-04-24 06:22:51

标签: python dynamic decorator

我有许多文件使用具有以下语法的类:

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

现在实现已从'get_XXX'和'set_XXX'变为'XXX':

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

这个实现是一个外部包,我不想改变。是否有可能编写一个包装器,它将动态拦截所有对'get_XXX'的调用,然后用新名称'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

它需要拦截所有调用,而不仅仅是有限的字段集,根据方法名称决定是否修改它并导致调用具有修改名称的方法。

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:1)

你可以'monkey patch'任何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)