Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 970
Uri Cohen Avatar asked Apr 24 '13 06:04

Uri Cohen


People also ask

How to rename a method in Python?

rename() method in Python is used to rename a file or directory. This method renames a source file/ directory to specified destination file/directory. Parameters: source: A path-like object representing the file system path.

Does Python allows function renaming?

The os. rename() method allows you to rename files in Python.

How do you rename a directory in Python?

Python rename() file is a method used to rename a file or a directory in Python programming and can be declared by passing two arguments named src (Source) and dest (Destination).


2 Answers

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:

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.

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. 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
like image 112
Blckknght Avatar answered Sep 25 '22 11:09

Blckknght


You can 'monkey patch' any python class; 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)
like image 24
Martijn Pieters Avatar answered Sep 25 '22 11:09

Martijn Pieters