Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically adding key-arguments to method

I would like to set the default key-arguments of an instance method dynamically. For example, with

class Module(object):
    def __init__(self, **kargs):
        set-default-key-args-of-method(self.run, kargs)  # change run arguments
    def run(self, **kargs):
        print kargs

We would have:

m = Module(ans=42)

m.run.im_func.func_code.co_argcount  # => 2
m.run.im_func.func_code.co_varnames  # => ('self','ans','kargs')
m.run.im_func.func_defaults          # => (42,)
m.run()                              # print {'ans':42}

I tried something with types.CodeType (which I don't really understand) for a function (not a method) and got it to work (well not-to-fail), but the added key-arguments did not show in the kargs dictionary of the function (it only print {})

The change has to be done for the current instance only. Actually, I am using a class right now (I'm OO in my mind) so I would like to do it with a class method, but a function is maybe better. Something like:

def wrapped_run(**kargs):
    def run(**key_args):
        print key_args

    return wrap-the-run-function(run, kargs) 

run = wrapped_run(ans=42)

run.func_code.co_argcount  # => 1
run.func_code.co_varnames  # => ('ans','key_args')  ## keep the 'key_args' or not
run.func_defaults          # => (42,)
run()                      # print {'ans':42}

Any advise or idea is welcome.

A little on the context:

The Module class is some kind a function wrapper, which can be use to include the lower-end function in a dataflow system automatically but add intermediate procedures. I would like the module run function (actually, it will probably be it's __call___ function) to have the correct API in order for the dataflow system to nicely generate the correct module's input transparently.

I'm using python 2.7

like image 286
Juh_ Avatar asked May 02 '13 12:05

Juh_


1 Answers

You might be looking for something like this:

class Module(object):
    def __init__(self, **kargs):
        old_run = self.run.im_func
        def run(self,**kwargs):
            kargs_local = kargs.copy()
            kargs.update(kwargs)
            return old_run(self,**kargs)
        self.run = run.__get__(self,Module)

    def run(self,**kargs):
        print kargs

m1 = Module(foo=3,bar='baz')
m1.run()
print type(m1.run)

m2 = Module(foo=4,qux='bazooka')
m2.run()
print type(m2.run)

I've just created a wrapper instancemethod around the previous function. (partially inspired by this post).

Alternatively:

from functools import partial
from types import MethodType

class Module(object):
    def __init__(self, **kargs):
        self.run = MethodType(partial(self.run.im_func,**kargs),self,Module)

    def run(self,**kargs):
        print kargs

but this still doesn't give the API you're looking for...

like image 179
mgilson Avatar answered Oct 06 '22 00:10

mgilson