如何动态修改函数的签名

时间:2019-06-11 15:16:01

标签: python-3.x arguments abstract-syntax-tree python-internals

我正在用Python编写框架。用户声明函数时,他们会这样做:

def foo(row, fetch=stuff, query=otherStuff)

def bar(row, query=stuff)

def bar2(row)

当后端看到query = value时,它将根据值使用query参数执行函数。这样,函数可以访问后端在其范围内完成的操作的结果。

目前,我每次通过检查查询,提取和其他项是否为None并使用一组与用户要求的参数完全匹配的args来启动参数。否则,我会收到“获得意外的关键字参数”错误。这是后端中的代码:

#fetch and query is something computed by the backend
if fetch= None and query==None:
    userfunction(row)
elif fetch==None:
    userunction (row, query=query)
elif query == None:
    userfunction (row, fetch=fetch)
else:
    userfunction (row,fetch=fetch,query=query)

这不好;对于后端提供的每个其他“服务”,我需要将所有组合与以前的组合一起编写。

相反,我想主要使用该函数并在执行它之前手动添加一个命名参数,删除执行这些检查的所有不必要的代码。然后,用户只需使用它真正想要的东西。

我不希望用户通过添加不需要的东西来修改功能(我也不希望他们每次都指定kwarg)。

因此,我想举一个可行的例子,一个将变量addNamedVar(name, function)添加到函数name的函数function

我想这样做,是因为用户函数被调用了很多次,这意味着它会触发我例如创建函数命名变量的字典(带有inspect),然后使用{ {1}}。我真的很想只修改一次函数,以避免任何形式的开销。

1 个答案:

答案 0 :(得分:0)

这确实在AST中是可行的,这就是我要做的,因为此解决方案将更适合我的用例。但是,通过使用函数克隆方法(如我所示的代码片段),您可以更轻松地完成我的要求。请注意,此代码返回具有相同默认值的相同功能。您可以使用此代码作为示例来执行所需的任何操作。 这适用于python3

def copyTransform(f, name, **args):
    signature=inspect.signature(f)
    params= list(signature.parameters)
    numberOfParam= len(params)
    numberOfDefault= len(f.__defaults__)
    listTuple= list(f.__defaults__)

    for key,val in args.items():
        toChangeIndex = params.index(key, numberOfDefault)
        if toChangeIndex:
            listTuple[toChangeIndex- numberOfDefault]=val

    newTuple= tuple(listTuple)
    oldCode=f.__code__

    newCode= types.CodeType(
        oldCode.co_argcount,             #   integer
        oldCode.co_kwonlyargcount,       #   integer
        oldCode.co_nlocals,              #   integer
        oldCode.co_stacksize,            #   integer
        oldCode.co_flags,                #   integer
        oldCode.co_code,                 #   bytes
        oldCode.co_consts,               #   tuple
        oldCode.co_names,                #   tuple
        oldCode.co_varnames,             #   tuple
        oldCode.co_filename,             #   string
        name,                            #   string
        oldCode.co_firstlineno,          #   integer
        oldCode.co_lnotab,               #   bytes
        oldCode.co_freevars,             #   tuple
        oldCode.co_cellvars              #   tuple
        )

    newFunction=types.FunctionType(newCode, f.__globals__, name, newTuple, f.__closure__)
    newFunction.__qualname__=name #also needed for serialization

如果要腌制克隆函数,则需要使用名称来做这些奇怪的事情。