Background
The typing module supports subscriptable types. Here is an example use case:
from typing import List
def foo(bar: List[int]):
pass
However, when you try to use this with isinstance, it fails:
>>> from typing import List
>>> my_list = [1, 2, 3]
>>> isinstance(my_list, List[int])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/typing.py", line 719, in __instancecheck__
return self.__subclasscheck__(type(obj))
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/typing.py", line 722, in __subclasscheck__
raise TypeError("Subscripted generics cannot be used with"
TypeError: Subscripted generics cannot be used with class and instance checks
Code
So, I wrote a program which can test for subscripted types. Here is my code:
class Union:
def __init__(self, type1, type2):
if not isinstance(type1, (type, Type)) or not isinstance(type2, (type, Type)):
raise ValueError(f'{type1!r}, {type2!r} are not types')
self.types = type1, type2
def __str__(self):
return self.types[0].__name__ + ' | ' + self.types[1].__name__
def __repr__(self):
return str(self)
@property
def __name__(self):
return str(self)
class Type:
def __init__(self, dtype, of=None):
if isinstance(dtype, Type):
self.dtype = dtype.dtype
elif isinstance(dtype, (type, Union)):
self.dtype = dtype
else:
raise ValueError(f'{dtype!r} is not a type')
if of is not None and not isinstance(of, (Type, Union, type)):
raise ValueError(f'{of!r} is not a type')
self.of = of
def __str__(self):
if self.of is not None:
return f'{self.dtype.__name__}[{str(self.of.__name__)}]'
else:
return str(self.dtype.__name__)
def __repr__(self):
return str(self)
def __getitem__(self, cls):
return Type(self, cls)
def __or__(self, other):
return Union(self, other)
def __ror__(self, other):
return Union(other, self)
@property
def __name__(self):
return str(self)
def IsInstance(obj, dtype):
if isinstance(dtype, type):
return isinstance(obj, dtype)
elif isinstance(dtype, Type):
if dtype.of is None:
return isinstance(obj, dtype.dtype)
return all(IsInstance(item, dtype.of) for item in obj)
elif isinstance(dtype, Union):
return any(IsInstance(obj, t) for t in dtype.types)
Bool = Type(bool)
Dict = Type(dict)
Float = Type(float)
Int = Type(int)
List = Type(list)
Set = Type(set)
Str = Type(str)
Tuple = Type(tuple)
Notes
Unionis the equivalent oftyping.Union, but it is a bit different. You can get aUnionby either:- Using
Union(int, float) - Using
Int | Float
- Using
- To do the instance check, use
IsInstance, notisinstance. For example:- Use
IsInstance([1, 2, 3], List[int])
- Use