Python 如何在运行时使用指定的参数名创建函数?

Python 如何在运行时使用指定的参数名创建函数?,python,function,arguments,Python,Function,Arguments,假设我有这个函数: def f(x,y): return x+y 如果我使用inspect.getargspec(f).args我会得到['x','y']。太好了 现在假设我想在运行时创建另一个函数g(a,b),在运行时之前我不知道参数名a和b: def g(a,b): return f(a,b) 有办法做到这一点吗?lambda几乎是正确的,只是我只能在编译时指定参数名 g = lambda *p: f(*p) 不知何故,我希望在运行时基于列表L(例如L=['a','b'])

假设我有这个函数:

def f(x,y):
   return x+y
如果我使用
inspect.getargspec(f).args
我会得到
['x','y']
。太好了

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

def g(a,b):
   return f(a,b)
有办法做到这一点吗?lambda几乎是正确的,只是我只能在编译时指定参数名

g = lambda *p: f(*p)

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

使用关键字参数怎么样

>>> 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
成为按名称传递的所有参数的字典。

您可以使用*args和**kwargs

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

def func():
    def dyn_func(*args, **kwargs):
        print args, kwargs
  return dyn_func
然后可以在生成的函数中使用args

f = func()
f(test=1)
将提供:

() {'test': 1}

然后就可以按照您的意愿管理args了

我感觉您需要这样的东西:

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
位置参数 ... rpc_命令('add',1,2) 3. >>> >>>#关键字args ... rpc_命令('multiply',x=20,y=6) 120 >>>#关键字args作为kwargs传递 ... rpc_命令('add',**{“a”:1,“b”:2}) 3. >>> >>>#混合args ... rpc_命令('multiply',5,y=6) 30 >>> >>>#不同的arg长度 ... rpc_命令('cube',3) 27 >>> >>>#作为位置参数传入最后一个 ... rpc_命令(‘勾股定理’,*[1,2,3]) 3.7416573867739413 >>> >>>#尝试一个不存在的函数 ... rpc_命令('doesntexist',5,6) 回溯(最近一次呼叫最后一次): 文件“”,第2行,在 rpc_命令中第6行的文件“” 未实现错误:未找到函数:doesntexist
这里有一种有点老套的方法,它首先通过修改从现有函数创建一个新函数,然后用它替换原来的代码。这主要是因为
types.CodeType()
调用有太多参数。Python3版本有些不同,因为许多
函数.func_code
属性被重命名,并且
类型.CodeType()的调用顺序略有改变

我是从@aaronasterling那里得到这个想法的(他说他是从MichaelFoord的名为。它可以很容易地做成一个装饰器,但根据你告诉我们的预期用途,我不认为这有什么帮助

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)))
导入系统 导入类型 def change_func_参数(函数、新参数): “”“创建一个新函数,将其参数重命名为新参数。”“” 如果sys.version_info[0]<3:#Python 2? code_obj=function.func_代码
断言(0不确定我是否理解了这个问题。我猜你可以使用
eval
语句创建
lambda
。。你能给出一个在运行时创建的函数的例子吗?你可以使用
FunctionType
CodeType
来构造对象,但我不确定我是否明白这一点。这听起来像是XY问题-在sa中me是动态命名的变量,通常意味着询问者需要一个列表,这感觉是解决问题的错误途径。我将随机猜测,并假设您的目的是在装饰器中保留参数名称,以便使用
inspect.getargspec
的文档工具提供一些有用的信息-在这种情况下,您需要module@Eric很好的猜测:)我的第一个猜测是:“如何从inspect中混淆函数args”Lolzt
**kwargs
的问题是,生成的字典的键并不总是按照您传递它们的顺序。值的任意顺序意味着您的第二个示例可以很容易地将它们传递给
f()
顺序错误,这在大多数情况下都很重要(尽管与显示的功能无关)。