Unpacking Python's Type Annotations

后端 未结 3 426
终归单人心
终归单人心 2021-01-04 13:52

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

相关标签:
3条回答
  • 2021-01-04 14:07

    Take note, this applies to Python 3.5.1

    For Python 3.5.2 take a look at phillip's answer.

    You shouldn't be checking with the identity operator as Phillip stated, use equality to get this right.

    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):

    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:

    signature.return_annotation.__parameters__[0] # int
    

    Now, in more complex scenarios i.e a class supplied as an argument with List[User] you must again extract the __parameter__[0] and then get the __forward_arg__. This is because Python wraps the argument in a special ForwardRef class:

    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).

    0 讨论(0)
  • 2021-01-04 14:15

    List is not a map of types to GenericMeta, despite the syntax. 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]. To compare two instances, you have multiple options:

    • Use ==, i.e., signature.return_annotation == List[int].
    • Store an instance of your type in a global variable and check against that, i.e.,

      a = List[int]
      def foo() -> a:
          pass
      inspect.signature(foo).return_annotation is a
      
    • Use 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.

    • Check against List only and read the contents yourself. 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].

    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 ==.

    0 讨论(0)
  • 2021-01-04 14:27

    Python 3.8 provides typing.get_origin() and typing.get_args() for this!

    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)
    

    See https://docs.python.org/3/library/typing.html#typing.get_origin

    0 讨论(0)
提交回复
热议问题