Python元编程:生成带有类型注释的函数签名

时间:2018-05-17 06:37:24

标签: python metaprogramming type-hinting

我在Python Web框架中工作,该框架使用Python 3类型注释进行验证和依赖注入。

所以我正在寻找一种方法来从生成函数的参数中生成带有类型注释的函数:

def gen_fn(args: Dict[str, Any]) -> Callable:
    def new_fn(???):
        pass
    return new_fn

这样

inspect.signature(gen_fn({'a': int}))

将返回

<Signature (a:int)>

是否有我提出的东西而不是???会做我需要的东西。

我还查看了inspect模块中的Signature.replace(),但没有找到将新签名附加到新功能或现有功能的方法。

我对使用ast犹豫不决,因为:

  

抽象语法本身可能随每个Python版本而改变

所以我的问题是:什么(如果有的话)是基于传递给生成函数的dict使用Python 3类型注释生成函数的合理方法?

修改:@Aran-Fey&#39; s solution正确回答我的问题,看来我的假设是错误的。更改签名不允许使用新签名调用new_fn。那是gen_fn({'a': int})(a=42)引发TypeError: ...`得到了一个意想不到的关键字参数&#39; a&#39;。

1 个答案:

答案 0 :(得分:3)

不是创建带注释的函数,而是更容易创建函数,然后手动设置注释。

  • inspect.signature在查看函数的实际签名之前查找是否存在__signature__属性,因此我们可以创建一个合适的inspect.Signature对象并将其分配到那里:< / p>

    params = [inspect.Parameter(param,
                                inspect.Parameter.POSITIONAL_OR_KEYWORD,
                                annotation=type_)
                            for param, type_ in args.items()]
    new_fn.__signature__ = inspect.Signature(params)
    
  • typing.get_type_hints 尊重__signature__,因此我们也应该更新__annotations__属性:

    new_fn.__annotations__ = args
    

将它们放在一起:

def gen_fn(args: Dict[str, Any]) -> Callable:
    def new_fn():
        pass

    params = [inspect.Parameter(param,
                                inspect.Parameter.POSITIONAL_OR_KEYWORD,
                                annotation=type_)
                            for param, type_ in args.items()]
    new_fn.__signature__ = inspect.Signature(params)
    new_fn.__annotations__ = args

    return new_fn

print(inspect.signature(gen_fn({'a': int})))  # (a:int)
print(get_type_hints(gen_fn({'a': int})))  # {'a': <class 'int'>}

请注意,这不会使您的函数可调用这些参数;所有这些只是烟雾和镜子,使函数看起来就像它有那些参数和注释。 实现该功能是一个单独的问题。

您可以使用varargs定义函数,将所有参数聚合为元组和字典:

def new_fn(*args, **kwargs):
    ...

但是这仍然会让你遇到实现函数体的问题。你没有说过这个函数在被调用时应该做什么,所以我无法帮助你。您可以查看this question以获取一些指示。