How to create a function at runtime with specified argument names?
Here's a somewhat hacky way to do it which first creates a new function from an existing one with the modification and then replaces the original's code with it. It's lengthly mostly because the types.CodeType()
call has so many arguments. The Python 3 version is somewhat different because a number of the function.func_code
attributes were renamed and the calling sequence of types.CodeType()
was changed slightly.
I got the idea from this answer by @aaronasterling (who says he got the idea from Michael Foord's Voidspace blog entry #583 titled Selfless Python). It could easily be made into a decorator, but I don't see that as being helpful based on what you've told us of the intended usage.
import sys
import types
def change_func_args(function, new_args):
""" Create a new function with its arguments renamed to new_args. """
if sys.version_info[0] < 3: # Python 2?
code_obj = function.func_code
assert(0 <= len(new_args) <= code_obj.co_argcount)
# The arguments are just the first co_argcount co_varnames.
# Rreplace them with the new argument names in new_args.
new_varnames = tuple(new_args[:code_obj.co_argcount] +
list(code_obj.co_varnames[code_obj.co_argcount:]))
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)
else: # Python 3
code_obj = function.__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(new_args[:code_obj.co_argcount] +
list(code_obj.co_varnames[code_obj.co_argcount:]))
new_code_obj = types.CodeType(code_obj.co_argcount,
code_obj.co_posonlyargcount,
code_obj.co_kwonlyargcount,
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)
modified = types.FunctionType(new_code_obj, function.__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)))
I have a feeling 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's kwargs
f_kwargs.update(kwargs)
return f(**f_kwargs)
Usage:
>>> # 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