如何动态修改函数的签名 [英] How to modify the signature of a function dynamically

查看:89
本文介绍了如何动态修改函数的签名的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am writing a framework in Python. When a user declares a function, they do:

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

def bar(row, query=stuff)

def bar2(row)

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

When the backend sees query= value, it executes the function with the query argument depending on value. This way the function has access to the result of something done by the backend in its scope.

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

Currently I build my arguments each time by checking whether query, fetch and the other items are None, and launching it with a set of args that exactly matches what the user asked for. Otherwise I got the "got an unexpected keyword argument" error. This is the code in the backend:

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

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

This is not good; for each additional "service" the backend offers, I need to write all the combinations with the previous ones.

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

Instead of that I would like to primarily take the function and manually add a named parameter, before executing it, removing all the unnecessary code that does these checks. Then the user would just use the stuff it really wanted.

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

I don't want the user to have to modify the function by adding stuff it doesn't want (nor do I want them to specify a kwarg every time).

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

So I would like an example of this if this is doable, a function addNamedVar(name, function) that adds the variable name to the function function.

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

I want to do that that way because the users functions are called a lot of times, meaning that it would trigger me to, for example, create a dict of the named var of the function (with inspect) and then using **dict. I would really like to just modify the function once to avoid any kind of overhead.

推荐答案

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

This is indeed doable in AST and that's what I am gonna do because this solution will suit better for my use case . However you could do what I asked more simply by having a function cloning approach like the code snippet I show. Note that this code return the same functions with different defaults values. You can use this code as example to do whatever you want. This works for 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

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

You need to do that weird stuff with the names if you want to Pickle your clone function.

这篇关于如何动态修改函数的签名的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆