Is there a function similar to isinstance
that can identify if some data is of hybrid types, for example (suppose such a function is named isinstance2
):
data = [1, 2, 3]
data2 = {'a', 'b', 'c'}
isinstance2(data, list[int]) # True
isinstance2(data2, list[int]) # False
isinstance2(data2, set[str]) # True
The use case is a class that checks types during instantiation:
class foo():
def __init__(self, data):
if isinstance2(data, list[str]):
# do stuff
else:
raise TypeError
If you want to generalise this kind of type-checking, you could maybe do something like the following:
from typing import Any, Sequence, Callable, Container
funcs_dict: dict[Any, Callable[[Any, Sequence[Any]], bool]] = {
list: lambda container, args: all(
isinstance(obj, args[0])
for obj in container
),
dict: lambda container, args: all(
isinstance(key, args[0]) and isinstance(value, args[1])
for key, value in container.items()
),
tuple: lambda container, args: all(
isinstance(tuple_elem, arg)
for tuple_elem, arg in zip(container, args)
)
# etc for as many types as you want to include
}
def runtime_generic_type_check(container: Container, annotation: Any) -> bool:
origin: Any = annotation.__origin__
args: Sequence[Any] = annotation.__args__
return (
isinstance(container, origin)
and funcs_dict[origin](container, args)
)
Examples:
>>> int_list = [1, 2]
>>> runtime_generic_type_check(int_list, list[int])
True
>>> from typing import List
>>> runtime_generic_type_check(int_list, List[int]
True
>>> runtime_generic_type_check(int_list, list[str]
False
>>> runtime_generic_type_check(int_list, tuple[int])
False
There are important caveats to this approach, however. This will essentially only work with very simple type hints — it won't work if you have a type hint like tuple[str, ...]
, nor will it work for a nested container, like dict[str, dict[str, int]]
. It also won't work for non-container type hints like Callable[[<parameters>], <return-type>]
. You could alter the function to take account of these limitations, but the code quickly becomes much more complex. Finally, this works on my python 3.9 shell but not on my python 3.6 shell — typing.List[str].__origin__
is typing.List
in python 3.6, while it is list
in python 3.9. I'm not sure when that change took place.