简体   繁体   English

解压 Python 的类型注解

[英]Unpacking Python's Type Annotations

I'm trying to generate some JavaScript based on the type annotations I have provided in on some Python functions by using the signature() function in the inspect module.我正在尝试根据我在某些 Python 函数中提供的类型注释生成一些 JavaScript,方法是使用inspect模块中的signature()函数。

This part works as I expect when the type is a simple builtin class:当类型是一个简单的内置类时,这部分按我的预期工作:

import inspect

def my_function() -> dict:
    pass

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

Though I'm not sure how to unwrap and inspect more complex annotations eg:虽然我不确定如何打开和检查更复杂的注释,例如:

from typing import List
import inspect

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

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

Again similar problem with forward referencing a custom class:前向引用自定义类再次出现类似问题:

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

What I'm looking to get out is something like this - so I can branch appropriately while generating the JavaScript:我希望得到的是这样的 - 所以我可以在生成 JavaScript 时进行适当的分支:

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

Thanks.谢谢。

List is not a map of types to GenericMeta , despite the syntax.尽管有语法, List不是类型到GenericMeta的映射。 Each access to it generates a new instance:每次访问它都会生成一个新实例:

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

This means that even List[int] is not List[int] .这意味着即使List[int] is not List[int] To compare two instances, you have multiple options:要比较两个实例,您有多种选择:

  • Use == , ie, signature.return_annotation == List[int] .使用== ,即signature.return_annotation == List[int]
  • Store an instance of your type in a global variable and check against that, ie,将您的类型的实例存储在全局变量中并进行检查,即,

     a = List[int] def foo() -> a: pass inspect.signature(foo).return_annotation is a
  • Use issubclass .使用issubclass The typing module defines that.打字模块定义了这一点。 Note that this might do more than you'd like, make sure to read the _TypeAlias documentation if you use this.请注意,这可能比您想要的更多,如果您使用它,请务必阅读_TypeAlias文档。

  • Check against List only and read the contents yourself.仅核对List并自己阅读内容。 Though the property is internal, it is unlikely that the implementation will change soon: List[int].__args__[0] contains the type argument starting from Python 3.5.2, and in earlier versions, its List[int].__parameters__[0] .尽管该属性是内部的,但实现不太可能很快改变: List[int].__args__[0]包含从 Python 3.5.2 开始的类型参数,在早期版本中,它的List[int].__parameters__[0]

If you'd like to write generic code for your exporter, then the last option is probably best.如果您想为导出器编写通用代码,那么最后一个选项可能是最好的。 If you only need to cover a specific use case, I'd personally go with using == .如果您只需要涵盖特定用例,我个人会使用==

Python 3.8 provides typing.get_origin() and typing.get_args() for this! Python 3.8 typing.get_origin()提供了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)

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

Take note, this applies to Python 3.5.1请注意,这适用于 Python 3.5.1

For Python 3.5.2 take a look at phillip's answer.对于 Python 3.5.2,请查看 phillip 的回答。

You shouldn't be checking with the identity operator as Phillip stated, use equality to get this right.您不应该像 Phillip 所说的那样与身份运算符核对,请使用相等来解决这个问题。

To check if a hint is a subclass of a list you could use issubclass checks (even though you should take note that this can be quirky in certain cases and is currently worked on):要检查提示是否是list的子类,您可以使用issubclass检查(尽管您应该注意这在某些情况下可能很古怪并且目前正在处理):

issubclass(List[int], list)  # True

To get the members of a type hint you generally have two watch out for the cases involved.要获得类型提示的成员,您通常需要注意所涉及的两个案例。

If it has a simple type, as in List[int] the value of the argument is located in the __parameters__ value:如果它具有简单类型,如在List[int]中,参数的值位于__parameters__值中:

signature.return_annotation.__parameters__[0] # int

Now, in more complex scenarios ie a class supplied as an argument with List[User] you must again extract the __parameter__[0] and then get the __forward_arg__ .现在,在更复杂的情况下,即作为参数提供的类List[User]必须再次提取__parameter__[0] ,然后获取__forward_arg__ This is because Python wraps the argument in a special ForwardRef class:这是因为 Python 将参数包装在一个特殊的ForwardRef类中:

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

Take note , you don't need to actually use inspect here, typing has a helper function named get_type_hints that returns the type hints as a dictionary (it uses the function objects __annotations__ attribute).请注意,您不需要在这里实际使用inspecttyping有一个名为get_type_hints的辅助函数, get_type_hints类型提示作为字典返回(它使用函数对象__annotations__属性)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM