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

Jas*_*n S 9 python arguments function

假设我有这个功能:

def f(x,y):
   return x+y
Run Code Online (Sandbox Code Playgroud)

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

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

def g(a,b):
   return f(a,b)
Run Code Online (Sandbox Code Playgroud)

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

g = lambda *p: f(*p)
Run Code Online (Sandbox Code Playgroud)

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

mar*_*eau 6

这是一种有点hacky的方法,它首先通过修改从现有函数创建一个新函数,然后用它替换原始代码。之所以冗长,主要是因为该types.CodeType()调用有太多参数。Python 3 版本有些不同,因为许多属性function.func_code被重命名,并且调用顺序也types.CodeType()略有改变。

我从 @aaronasterling 的回答中得到了这个想法(他说他是从 Michael Foord 的 Voidspace 博客条目 #583 标题为Selfless Python中得到这个想法的)。它可以很容易地制作成装饰器,但根据您告诉我们的预期用途,我认为这没有帮助。

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)))
Run Code Online (Sandbox Code Playgroud)