Creating a python function at run time with the specified argument names

Suppose I have this function:

def f(x,y): return x+y 

If I use inspect.getargspec(f).args , I get ['x','y'] as a result. Excellent.

Now suppose I want to create another function g(a,b) at runtime, where I don't know the names of the arguments a and b until runtime:

 def g(a,b): return f(a,b) 

Is there any way to do this? Lambdas is almost right, except that I can assign argument names at compile time.

 g = lambda *p: f(*p) 

Somehow I want to dynamically create a function at runtime based on the list L (for example, L=['a','b'] ), so inspect.getargspec(g).args == L

+6
source share
4 answers

Here is a somewhat hacky way to do this, which first creates a new function from an existing one with a modification, and then replaces the source code with it. This is mainly because the types.CodeType() call has so many arguments. The Python 3 version (not shown) that I also implemented is slightly different from the fact that several function.func_code attributes got renamed, and the calling sequence types.CodeType() was slightly changed.

I got an idea from this answer from @aaronasterling (who said they got the idea from Michael Ford's Voidspace blog called Selfless Python ). It can be easily turned into a decorator, but I don’t see it useful, based on what you told us about the intended use.

 import types def change_func_args(function, new_args): """ Create a new function with its arguments renamed to new_args. """ code_obj = function.func_code assert(0 <= len(new_args) <= code_obj.co_argcount) # the arguments are just the first co_argcount co_varnames # replace them with the new argument names in new_args new_varnames = tuple(list(new_args[:code_obj.co_argcount]) + list(code_obj.co_varnames[code_obj.co_argcount:])) # type help(types.CodeType) at the interpreter prompt for information new_code_obj = types.CodeType(code_obj.co_argcount, code_obj.co_nlocals, code_obj.co_stacksize, code_obj.co_flags, code_obj.co_code, code_obj.co_consts, code_obj.co_names, new_varnames, code_obj.co_filename, code_obj.co_name, code_obj.co_firstlineno, code_obj.co_lnotab, code_obj.co_freevars, code_obj.co_cellvars) modified = types.FunctionType(new_code_obj, function.func_globals) function.__code__ = modified.__code__ # replace code portion of original if __name__ == '__main__': import inspect def f(x, y): return x+y def g(a, b): return f(a, b) print('Before:') print('inspect.getargspec(g).args: {}'.format(inspect.getargspec(g).args)) print('g(1, 2): {}'.format(g(1, 2))) change_func_args(g, ['p', 'q']) print('') print('After:') print('inspect.getargspec(g).args: {}'.format(inspect.getargspec(g).args)) print('g(1, 2): {}'.format(g(1, 2))) 

Output:

 Before: inspect.getargspec(g).args: ['a', 'b'] g(1, 2): 3 After: inspect.getargspec(g).args: ['p', 'q'] g(1, 2): 3 
+3
source

How to use keyword arguments?

 >>> g = lambda **kwargs: kwargs >>> g(x=1, y=2) {'y': 2, 'x': 1} >>> g(a='a', b='b') {'a': 'a', 'b': 'b'} 

Sort of:

 g = lambda **kwargs: f(kwargs.get('a', 0), kwargs['b']) 

or say that you want to use only the values:

 >>> g = lambda **kwargs: f(*kwargs.values()) >>> def f(*args): print sum(args) ... >>> g(a=1, b=2, c=3) 6 

In any case, using the **kwargs syntax causes kwargs be the dictionary of all arguments passed by name.

+1
source

I have the feeling that you want something like this:

 import inspect import math def multiply(x, y): return x * y def add(a, b): return a + b def cube(x): return x**3 def pythagorean_theorum(a, b, c): return math.sqrt(a**2 + b**2 + c**2) def rpc_command(fname, *args, **kwargs): # Get function by name f = globals().get(fname) # Make sure function exists if not f: raise NotImplementedError("function not found: %s" % fname) # Make a dict of argname: argvalue arg_names = inspect.getargspec(f).args f_kwargs = dict(zip(arg_names, args)) # Add kwargs to the function kwargs f_kwargs.update(kwargs) return f(**f_kwargs) 

Using:

 >>> # Positional args ... rpc_command('add', 1, 2) 3 >>> >>> # Keyword args ... rpc_command('multiply', x=20, y=6) 120 >>> # Keyword args passed as kwargs ... rpc_command('add', **{"a": 1, "b": 2}) 3 >>> >>> # Mixed args ... rpc_command('multiply', 5, y=6) 30 >>> >>> # Different arg lengths ... rpc_command('cube', 3) 27 >>> >>> # Pass in a last as positional args ... rpc_command('pythagorean_theorum', *[1, 2, 3]) 3.7416573867739413 >>> >>> # Try a non-existent function ... rpc_command('doesntexist', 5, 6) Traceback (most recent call last): File "<stdin>", line 2, in <module> File "<stdin>", line 6, in rpc_command NotImplementedError: function not found: doesntexist 
+1
source

You can use * args and ** kwargs

let's say you create a dynamic function at runtime

 def func(): def dyn_func(*args, **kwargs): print args, kwargs return dyn_func 

then you can use args in your generated function

 f = func() f(test=1) 

will give:

 () {'test': 1} 

then you can control args as you wish

0
source

All Articles