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

Using ast to create a function with a keyword-only argument that has a default value

我正在尝试使用 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, lineno=1, col_offset=0),
                ],
                posonlyargs=[],
            ),
            body=[ast.Return(
                value=ast.Name(id='x', ctx=ast.Load(), lineno=1, col_offset=0),
                lineno=1,
                col_offset=0,
            )],
            decorator_list=[],
            lineno=1,
            col_offset=0,
        )
    ],
    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) returns 100,但调用 f() 会产生:

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

我怀疑问题出在我将 AST 转换为函数的过程中。我在这里看到了另一个问题的方法(不幸的是我没有 link )。它看起来有点狡猾,但我不确定该怎么做。

函数的默认参数值不是函数代码对象的一部分。它们不可能,因为默认值是在函数定义时创建的,而不是字节码编译时。对于 non-keyword-only 个参数,默认参数值存储在 __defaults__ 中,对于 keyword-only 个参数,默认参数值存储在 __kwdefaults__ 中。当您从 module_code 中提取 f_code 时,您不会获得任何有关默认值的信息。

执行函数定义,然后获取实际的函数对象:

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