解压缩Python的类型注释

时间:2016-08-01 16:16:43

标签: python python-3.x annotations inspect type-hinting

我尝试使用signature()模块中的inspect函数,根据我在某些Python函数中提供的类型注释生成一些JavaScript。

当类型是一个简单的内置类时,这部分可以正常工作:

import inspect

def my_function() -> dict:
    pass

signature = inspect.signature(my_function)
signature.return_annotation is dict  # True

虽然我不确定如何打开和检查更复杂的注释,例如:

from typing import List
import inspect

def my_function() -> List[int]:
    pass

signature = inspect.signature(my_function)
signature.return_annotation is List[int]  # False

再次引用自定义类的类似问题:

def my_function() -> List['User']:
    pass
...
signature.return_annotation  # typing.List[_ForwardRef('User')]

我想要离开的是这样的 - 所以我可以在生成JavaScript时适当地分支:

type = signature.return_annotation... # list
member_type = signature.return_annotation... # int / 'User'

感谢。

3 个答案:

答案 0 :(得分:2)

List不是GenericMeta类型的地图,尽管有语法。每次访问都会生成一个新实例:

>>> [ id(List[str]) for i in range(3) ]
[33105112, 33106872, 33046936]

这意味着即使List[int] is not List[int]。要比较两个实例,您有多个选项:

  • 使用==,即signature.return_annotation == List[int]
  • 将您的类型的实例存储在全局变量中,然后检查它,即

    a = List[int]
    def foo() -> a:
        pass
    inspect.signature(foo).return_annotation is a
    
  • 使用issubclass。打字模块定义了。请注意,这可能比您更喜欢,请务必阅读_TypeAlias文档。

  • 仅检查List并自行阅读内容。虽然属性是内部的,但实现不太可能很快改变:List[int].__args__[0]包含从Python 3.5.2开始的类型参数,在早期版本中包含List[int].__parameters__[0]

如果您想为导出器编写通用代码,那么最后一个选项可能是最佳选择。如果您只需要涵盖特定用例,我个人可以使用==

答案 1 :(得分:2)

请注意,这适用于Python 3.5.1

对于Python 3.5.2,请看一下phillip的答案。

您不应该像Phillip所说的那样使用身份运营商进行检查,使用相等来做到这一点。

要检查提示是否是list的子类,您可以使用issubclass检查(即使您应该注意在某些情况下这可能是古怪的并且目前正在处理):< / p>

issubclass(List[int], list)  # True

要获得类型提示的成员,您通常需要注意所涉及的案例。

如果它有一个简单类型,就像在List[int]中一样,参数的值位于__parameters__值中:

signature.return_annotation.__parameters__[0] # int

现在,在更复杂的场景中,即作为List[User]的参数提供的类,您必须再次提取__parameter__[0],然后获取__forward_arg__。这是因为Python将参数包装在一个特殊的ForwardRef类中:

d = signature.return_annotation.__parameter__[0]
d.__forward_arg__ # 'User'

记下,这里不需要实际使用inspecttyping有一个名为get_type_hints的辅助函数,它返回类型提示为字典(它使用函数对象__annotations__属性)。

答案 2 :(得分:0)

Python 3.8为此提供了typing.get_origin()typing.get_args()

assert get_origin(Dict[str, int]) is dict
assert get_args(Dict[int, str]) == (int, str)

assert get_origin(Union[int, str]) is Union
assert get_args(Union[int, str]) == (int, str)

请参见https://docs.python.org/3/library/typing.html#typing.get_origin