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 XXX:

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

This implementation is an external package that I do not want to modify. Is it possible to write a wrapper that will "on the fly" intercept all calls to "get_XXX" and instead replace it 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 

He needs to intercept all the calls, and not just the finite set of fields, make a decision based on the name of the method, if you change it, and call the method with the changed name, which will be called.

+4
source share
2 answers

If you want to continue to use get_Field and set_Field for an object that has switched to using properties (where you simply open or assign Field ), you can use a wrapper object:

 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 use additional syntax, such as indexing or iterating an object, or if you need to recognize the type of an object using isinstance .

A more complicated solution would be to create a subclass that rewrites the name and forces the object to use it. This is not exactly packaging, as external code will still process the object directly (and therefore magic methods and isinstance ) will work as expected. This approach will work for most objects, but it may fail for types that have fantastic metaclass magic, and for some built-in 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 
+4
source

You can 'monkey patch' any python class; import the class directly and add the property:

 import original_module @property def get_Field(self): return self.Field original_module.OriginalClass.get_Field = get_Field 

You will need to list which fields you would like to receive in 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) 
+1
source

All Articles