解压 Python 的类型注解
Unpacking Python's Type Annotations
我正在尝试根据我在某些 Python 函数中提供的类型注释生成一些 JavaScript,方法是使用 inspect
中的 signature()
函数模块。
当类型是一个简单的内置函数时,这部分工作如我所料 class:
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
前向引用自定义时再次出现类似问题 class:
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'
谢谢。
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
。 typing 模块定义了它。请注意,这可能比您想要的更多,如果您使用它,请务必阅读 _TypeAlias
文档。
- 仅对照
List
检查并自己阅读内容。虽然 属性 是内部的,但实现不太可能很快改变:List[int].__args__[0]
包含从 Python 3.5.2 开始的类型参数,在早期版本中,它的 List[int].__parameters__[0]
.
如果您想为您的导出器编写通用代码,那么最后一个选项可能是最好的。如果您只需要涵盖特定用例,我个人会使用 ==
.
请注意,这适用于 Python 3.5.1
对于Python 3.5.2 看看phillip的回答
您不应该像 Phillip 所说的那样使用身份运算符进行检查,使用相等来获得正确的结果。
要检查提示是否是 list
的子class,您可以使用 issubclass
检查(尽管您应该注意,在某些情况下这可能很古怪,并且目前正在处理):
issubclass(List[int], list) # True
要获得类型提示的成员,您通常需要两个注意所涉及的情况。
如果它有一个简单类型,如 List[int]
参数的值位于 __parameters__
值中:
signature.return_annotation.__parameters__[0] # int
现在,在更复杂的情况下,即作为参数提供的 class 与 List[User]
一起,您必须再次提取 __parameter__[0]
,然后获取 __forward_arg__
。这是因为 Python 将参数包装在特殊的 ForwardRef
class:
中
d = signature.return_annotation.__parameter__[0]
d.__forward_arg__ # 'User'
注意,你不需要在这里实际使用inspect
,typing
有一个名为get_type_hints
的辅助函数returns 类型提示作为字典(它使用函数对象 __annotations__
属性)。
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
我正在尝试根据我在某些 Python 函数中提供的类型注释生成一些 JavaScript,方法是使用 inspect
中的 signature()
函数模块。
当类型是一个简单的内置函数时,这部分工作如我所料 class:
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
前向引用自定义时再次出现类似问题 class:
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'
谢谢。
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
。 typing 模块定义了它。请注意,这可能比您想要的更多,如果您使用它,请务必阅读_TypeAlias
文档。- 仅对照
List
检查并自己阅读内容。虽然 属性 是内部的,但实现不太可能很快改变:List[int].__args__[0]
包含从 Python 3.5.2 开始的类型参数,在早期版本中,它的List[int].__parameters__[0]
.
如果您想为您的导出器编写通用代码,那么最后一个选项可能是最好的。如果您只需要涵盖特定用例,我个人会使用 ==
.
请注意,这适用于 Python 3.5.1
对于Python 3.5.2 看看phillip的回答
您不应该像 Phillip 所说的那样使用身份运算符进行检查,使用相等来获得正确的结果。
要检查提示是否是 list
的子class,您可以使用 issubclass
检查(尽管您应该注意,在某些情况下这可能很古怪,并且目前正在处理):
issubclass(List[int], list) # True
要获得类型提示的成员,您通常需要两个注意所涉及的情况。
如果它有一个简单类型,如 List[int]
参数的值位于 __parameters__
值中:
signature.return_annotation.__parameters__[0] # int
现在,在更复杂的情况下,即作为参数提供的 class 与 List[User]
一起,您必须再次提取 __parameter__[0]
,然后获取 __forward_arg__
。这是因为 Python 将参数包装在特殊的 ForwardRef
class:
d = signature.return_annotation.__parameter__[0]
d.__forward_arg__ # 'User'
注意,你不需要在这里实际使用inspect
,typing
有一个名为get_type_hints
的辅助函数returns 类型提示作为字典(它使用函数对象 __annotations__
属性)。
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