使用ast创建具有仅关键字参数且具有默认值的函数

问题描述

我正在尝试使用ast动态创建一个具有认值的仅关键字参数函数。但是,结果函数仍然需要参数,并且如果未传递,则会引发TypeError

这是创建函数f代码

import ast
import types

module_ast = ast.Module(
    body=[
        ast.FunctionDef(
            name='f',args=ast.arguments(
                args=[],vararg=None,kwarg=None,defaults=[],kwonlyargs=[
                    ast.arg(
                        arg='x',lineno=1,col_offset=0,),],kw_defaults=[
                    ast.Num(n=42,col_offset=0),posonlyargs=[],body=[ast.Return(
                value=ast.Name(id='x',ctx=ast.Load(),)],decorator_list=[],)
    ],type_ignores=[],)

module_code = compile(module_ast,'<ast>','exec')

# This is the part that I'm suspicIoUs of
f_code = next(c for c in module_code.co_consts if isinstance(c,types.CodeType))

f = types.FunctionType(
    f_code,{}
)

如果我print(ast.unparse(module_ast)),我会得到期望的结果:

def f(*,x=42):
    return x

调用f(x=100)会按预期返回100,但是调用f()会产生:

TypeError: f() missing 1 required keyword-only argument: 'x'

我怀疑问题在于将AST转换为功能。我在这里的另一个问题中看到了这种方法(很遗憾,我没有链接)。看起来有点狡猾,但我不确定该怎么做。

解决方法

函数的默认参数值不是该函数的代码对象的一部分。不能这样,因为默认值是在函数定义时创建的,而不是在字节码编译时创建的。非纯关键字参数的默认参数值存储在__defaults__中,纯关键字参数的默认参数值存储在__kwdefaults__中。从f_code中提取module_code时,您不会获得任何有关默认值的信息。

执行函数定义,然后检索实际的函数对象:

namespace = {}
exec(module_code,namespace)
function = namespace['f']