Check a variable against Union type at runtime in Python 3.6
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
.
The existing accepted answer by MSeifert (https://stackoverflow.com/a/45959000/7433423) does not distinguish Union
s 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
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.