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

creating a python function at runtime with specified argument names

我想这个功能:

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

如果我使用inspect.getargspec(f).argsi get ['x','y']as a result。伟大的。

现在想一想创建另一个函数g(a,b)在运行时,在我不知道的名称和参数ab直到运行时:

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

有没有办法这样做吗?Lambda表达式是几乎是正确的,但我只能在编译时指定参数名称。

1
g = lambda *p: f(*p)

我想创建一个函数在运行时基于一dynamically列表L(例如L=['a','b']),这样inspect.getargspec(g).args == L


这是一种有点老套的方法,首先用修改创建一个新的函数,然后用它替换原来的代码。这主要是因为types.CodeType()调用有太多的参数。我还实现的python 3版本(未显示)有些不同,因为重命名了一些thefunction.func_code属性,并且稍微更改了types.CodeType()的调用顺序。

我从@aaronasterling的回答中得到了这个想法(他说这个想法是从MichaelFoord的空间博客"无私的Python"中得到的)。它很容易成为一个装饰师,但我不认为这是有帮助的基础上,你告诉我们的预期用途。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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)))

输出:

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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)

用途:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
>>> # 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
2
3
4
5
>>> g = lambda **kwargs: kwargs
>>> g(x=1, y=2)
{'y': 2, 'x': 1}
>>> g(a='a', b='b')
{'a': 'a', 'b': 'b'}

类似:

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

或者假设您只想使用以下值:

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

在任何情况下,使用**kwargs语法都会导致kwargs成为一个字典,其中包含按名称传递的所有参数。


您可以使用*args和*kwargs

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

1
2
3
4
def func():
    def dyn_func(*args, **kwargs):
        print args, kwargs
  return dyn_func

然后可以在生成的函数中使用参数

1
2
f = func()
f(test=1)

将给予:

1
() {'test': 1}

那么就可以根据您的需要管理arg了