I have a generic class inside which I need to use the type variable to check parameter types. Inside the validate()
method I assign it to the ItemClass
variable:
"""Test generic class and usage of the generic type inside."""
from __future__ import annotations
from typing import List, TypeVar, Sequence, Iterator, Callable
T = TypeVar("T")
class ListFromItem(List[T]):
"""Generic class for Pydantic lists which can be created from a single item."""
@classmethod
def __get_validators__(cls) -> Iterator[Callable]:
yield cls.validate
@classmethod
def validate(cls, data):
"""Validate arguments for the constructor and call the constructor."""
# How to get the class from the type variable T?
ItemClass = cls.__orig_bases__[0].__args__[0]
if isinstance(data, ItemClass):
return cls((data,))
if (
isinstance(data, Sequence)
and all(isinstance(item, ItemClass) for item in data)):
return cls(data)
raise TypeError(f'{ItemClass} or Sequence[{ItemClass}] required.')
Click to see an example (runnable code).
The code is runnable with the code above.
class ListOfIntegers(ListFromItem[int]):
"""Provide list of integers for Pydantic (instantiatable from a list or integer)."""
pass
print(ListOfIntegers.validate(5))
print(ListOfIntegers.validate([5]))
try:
print(ListOfIntegers.validate('a'))
except TypeError as exception:
print(f'Expected exception: {exception!r}')
else:
assert False, 'Failure: TypeError not thrown'
results:
[5]
[5]
Expected exception: TypeError("<class 'int'> or Sequence[<class 'int'>] required.")
Is the following code the best way how to get the class from the type variable T
?
ItemClass = cls.__orig_bases__[0].__args__[0]
To improve the readability of the code I would much prefer to use the identifier T
to refer to T
instead of the difficult-to-understand dunder properties.
In addition to that Pylint complains about the code:
$ pylint tmp/generic_class.py
************* Module generic_class
tmp/generic_class.py:19:20: E1101: Class 'ListFromItem' has no '__orig_bases__' member (no-member)
Is this just a problem inside Pylint or can I expect more problems to pop up?
Click to see my failed attempts.
ItemClass = T
...
File "/home/vbrozik/dev/asa-csm-clean/tmp/generic_class.py", line 22, in validate
if isinstance(data, ItemClass):
TypeError: isinstance() arg 2 must be a type, a tuple of types, or a union
ItemClass = Type[T]
...
File "/home/vbrozik/dev/asa-csm-clean/tmp/generic_class.py", line 22, in validate
if isinstance(data, ItemClass):
File "/usr/lib/python3.10/typing.py", line 994, in __instancecheck__
return self.__subclasscheck__(type(obj))
File "/usr/lib/python3.10/typing.py", line 997, in __subclasscheck__
raise TypeError("Subscripted generics cannot be used with"
TypeError: Subscripted generics cannot be used with class and instance checks