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

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

问题描述

我正在用 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.

目前,我每次都通过检查 query、fetch 和其他项目是否为 None 来构建我的参数,并使用一组与用户要求的参数完全匹配的参数启动它.否则,我会收到出现意外的关键字参数"错误.这是后端的代码:

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.

我想这样做是因为用户函数被多次调用,这意味着它会触发我,例如,创建函数的命名变量的字典(使用检查),然后使用 <代码>**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

如果你想 Pickle 你的克隆函数,你需要对名称做一些奇怪的事情.

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

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

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