使用指定的参数名称在运行时创建python函数

时间:2013-12-20 21:43:39

标签: python function arguments

假设我有这个功能:

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

如果我使用inspect.getargspec(f).args,我会得到['x','y']。大。

现在假设我想在运行时创建另一个函数g(a,b),在运行时我不知道参数名ab

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

有办法做到这一点吗? Lambdas几乎是正确的,除了我只能在编译时分配参数名称。

g = lambda *p: f(*p)

不知何故,我想在运行时基于列表L(例如L=['a','b'])动态创建函数,以便inspect.getargspec(g).args == L

4 个答案:

答案 0 :(得分:3)

这是一种有点hacky的方法,它首先从现有的一个修改后创建一个新函数,然后用它替换原始代码。这主要是因为types.CodeType()调用有很多参数。我实现的Python 3版本(未显示)有些不同,因为重命名了许多function.func_code属性,并且types.CodeType()的调用序列稍有改变。

我从this answer得到了@aaronasterling的想法(他说他从Michael Foord的Voidspace博客中得到了这个想法,标题为Selfless Python)。它可以很容易地变成装饰者,但我不认为根据你告诉我们的预期用途有帮助。

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)))

输出:

Before:
inspect.getargspec(g).args: ['a', 'b']
g(1, 2): 3

After:
inspect.getargspec(g).args: ['p', 'q']
g(1, 2): 3

答案 1 :(得分:1)

如何使用关键字参数?

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

类似的东西:

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

或者假设您只想使用值:

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

在任何情况下,使用**kwargs语法会导致kwargs成为通过名称传递的所有参数的字典。

答案 2 :(得分:1)

我觉得你想要这样的东西:

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)

用法:

>>> # 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

答案 3 :(得分:0)

您可以使用* args和** kwargs

假设您在运行时生成动态函数

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

然后可以将args用于生成的函数

f = func()
f(test=1)

会给:

() {'test': 1}

然后可以根据需要管理args