Check a variable against Union type at runtime in Python 3.6

后端 未结 4 1237
春和景丽
春和景丽 2020-12-10 01:36

UPDATE (September 2020): Python 3.9 includes the typing.get_type_hints function for this use case, see https://docs.python.org/3.9/library/typ

相关标签:
4条回答
  • 2020-12-10 01:52

    The existing accepted answer by MSeifert (https://stackoverflow.com/a/45959000/7433423) does not distinguish Unions from other generic types, and it is difficult to determine at runtime whether a type annotation is a Union or some other generic type like Mapping due to the behavior of isinstance() and issubclass() on parameterized Union types.

    It appears that generic types will have an undocumented __origin__ attribute which will contain a reference to the original generic type used to create it. Once you have confirmed that the type annotation is a parameterized Union, you can then use the also undocumented __args__ attribute to get the type parameters.

    >>> from typing import Union
    >>> type_anno = Union[int, str]
    >>> type_anno.__origin__ is Union
    True
    >>> isinstance(3, type_anno.__args__)
    True
    >>> isinstance('a', type_anno.__args__)
    True
    
    0 讨论(0)
  • 2020-12-10 01:54

    You could use the __args__ attribute of Union which holds a tuple of the "possible contents:

    >>> from typing import Union
    
    >>> x = Union[int, str]
    >>> x.__args__
    (int, str)
    >>> isinstance(3, x.__args__)
    True
    >>> isinstance('a', x.__args__)
    True
    

    The __args__ argument is not documented so it could be considered "messing with implementation details" but it seems like a better way than parsing the repr.

    0 讨论(0)
  • 2020-12-10 01:54

    In Python 3.8 and later, the approach suggested by MSeifert and Richard Xia can be improved by not using the undocumented attributes __origin__ and __args__. This functionality is provided by the new functions typing.get_args(tp) and typing.get_origin(tp):

    >> from typing import Union, get_origin, get_args
    >> x = Union[int, str]
    >> get_origin(x), get_args(x)
    (typing.Union, (<class 'int'>, <class 'str'>))
    >> get_origin(x) is Union
    True
    >> isinstance(3, get_args(x))
    True
    >> isinstance('a', get_args(x))
    True
    >> isinstance([], get_args(x))
    False
    

    P.S.: I know that the question is about Python 3.6 (probably because this was the newest version at the time), but I arrived here when I searched for a solution as a Python 3.8 user. I guess that others might be in the same situation, so I thought that adding a new answer here makes sense.

    0 讨论(0)
  • 2020-12-10 02:09

    You can use the typeguard module which can be installed with pip. It provides you with a function check_argument_types or a function decorator @typechecked. which should do your runtime type checking for you: https://github.com/agronholm/typeguard

    from typing import Union
    from typeguard import check_argument_types, typechecked
    
    def check_and_do_stuff(a: Union[str, int]) -> None:
        check_argument_types() 
        # do stuff ...
    
    @typechecked
    def check_decorator(a: Union[str, int]) -> None:
        # do stuff ...
    
    check_and_do_stuff("hello")
    check_and_do_stuff(42)
    check_and_do_stuff(3.14)  # raises TypeError
    

    If you want to check a type of a single variable for a different reason, you can use typeguard's check_type function directly:

    from typing import Union
    from typeguard import check_type
    
    MyType = Union[str, int]
    
    check_type("arg", "string", MyType, None)  # OK
    check_type("arg", 42, MyType, None)  # OK
    check_type("arg", 3.5, MyType, None)  # raises TypeError
    

    The "arg" and None arguments are unused in this example. Note that the check_type function is not documented as a public function of this module so its API may be subject to change.

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