简体   繁体   English

Python:即时重命名方法名称

[英]Python: renaming method names on-the-fly

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': 现在实现已从“ get_XXX”和“ set_XXX”更改为“ 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'? 是否可以编写一个包装程序,该包装程序将即时拦截所有对“ 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

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. 它需要拦截所有调用,而不仅仅是拦截有限的字段,根据方法名称决定是否修改它,并导致调用具有修改名称的方法。

If you want to continue to use get_Field and set_Field on an object that has switched to using properties (where you simply access or assign to Field ), it's possible to use an wrapper object: 如果要继续在已切换为使用属性的对象上使用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)

This will have problems if you are using extra syntax, like indexing or iteration on the object, or if you need to recognize the type of the object using isinstance . 如果您使用额外的语法(例如,对对象进行索引或迭代),或者需要使用isinstance识别对象的类型,则将出现问题。

A more sophisticated solution would be to create a subclass that does the name rewriting and force the object to use it. 一个更复杂的解决方案是创建一个子类来重写名称并强制对象使用它。 This isn't exactly a wrapping, since outside code will still deal with the object directly (and so magic methods and isinstance ) will work as expected. 这并不是完全包装,因为外部代码仍将直接处理该对象(因此,魔术方法和isinstance )将按预期工作。 This approach will work for most objects, but it might fail for types that have fancy metaclass magic going on and for some builtin types: 这种方法适用于大多数对象,但是对于花哨的元类魔术不断出现的类型以及某些内置类型而言,它可能会失败:

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

You can 'monkey patch' any python class; 您可以“猴子修补”任何python类; import the class directly and add a property: 直接导入类并添加属性:

import original_module

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

original_module.OriginalClass.get_Field = get_Field

You'd need to enumerate what fields you wanted to access this way: 您需要枚举要通过这种方式访问​​哪些字段:

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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM