问题描述
我正在尝试使用ast
动态创建一个具有默认值的仅关键字参数的函数。但是,结果函数仍然需要参数,并且如果未传递,则会引发TypeError
。
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']