felisimo
felisimo

Reputation: 364

Run python commands upon accessing an object

I have the following command:

a = imp.load_source("a", r"some_path\some_source.py")

and so a is a module object from which I can access and run all functions inside some_source.py. Is there a way in python to accomplish that additional functionality will run before using any of some_source's functions but without changing some_source.py? For instance if some_source.py has functions foo1, foo2 ... foo100, then I want the command

a.foo5() 

to actually execute:

imp.reload(a)
a.foo(5)

and this should be applied to all 100 foo functions.

Just to be clear, I want the added functionality to happen simply by running the same command as everybody in my team are used to - a.foo5(). I don't want them to have to create some sort of class or call a different method than what they're used to.

Upvotes: 2

Views: 249

Answers (2)

Rohi
Rohi

Reputation: 814

You could do the following :

Option A :

class example:
    def __init__(self):
        self.a = 2
        self.b = 3

    def some_func(self, argument_a):
        print(argument_a)

    def some_func_2(self, argument_b):
        print(argument_b)


def make_method(method_name):
    def _method(self, *args):
        # Insert what ever extra stuff you want to do here ...
        function_to_call = getattr(self.class_reference, method_name)
        function_to_call(self, *args)
    return _method


class a_class:
    def __init__(self, class_reference):
        self.class_reference = class_reference
        object_method_list = [func for func in dir(object) if callable(getattr(object, func))]
        method_list = [func for func in dir(class_reference) if callable(getattr(class_reference, func))]
        for method_name in method_list:
            if method_name in object_method_list:
                continue

            _method = make_method(method_name)
            setattr(type(self), method_name, _method)


module_class = a_class(example)
module_class.some_func()

This creates a class that creates all function from a dynamically (with an extra something).

Option B :

def do_func_with_extra(function_to_do, *args):
    # Do something extra...
    function_to_do(*args)
do_func_with_extra(a.some_func, argument0, argument1, ..)

Option B isnt exactly as you asked, but it would work as well.

I am also pretty certain there is a better way. but not one I can think of right now. Let me know if it works for you.

Upvotes: 0

Thierry Lathuille
Thierry Lathuille

Reputation: 24233

You could try to decorate your module like this:

class ReloadingModule:
    def __init__(self, module):
        self.module = module

    def __getattr__(self, attr):
        print("reloading {}...".format(self.module.__name__))
        imp.reload(self.module)
        return getattr(self.module, attr)


import this
this = ReloadingModule(this)

print('\n\n',this.c, '\n\n')
print('\n\n',this.s, '\n\n')

Output:

reloading this...
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
[...]


 97 


reloading this...
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
[...]

 Gur Mra bs Clguba, ol Gvz Crgref

Ornhgvshy vf orggre guna htyl.
Rkcyvpvg vf orggre guna vzcyvpvg.
Fvzcyr vf orggre guna pbzcyrk.
[...]

With some math:

import math
math = ReloadingModule(math)

print(math.pi)
print(math.sin(math.pi/2))

Output:

reloading math...
3.141592653589793
reloading math...
reloading math...
1.0

Upvotes: 3

Related Questions