方法一:
from types import UnionType
from typing import TypeVar, Generic, TypeModelT = TypeVar('ModelT')def _new_cls_with_grm_generic_args(cls, __item):new_cls = type(f"{cls.__name__}[{__item.__name__}]", (cls,), {})new_cls._grm_generic_arg = __itemreturn new_clsclass GenericResolveMeta(type):def __getitem__(cls, __item):print('__getitem__')return _new_cls_with_grm_generic_args(cls, __item)class BaseDAO(Generic[ModelT], metaclass=GenericResolveMeta):"""DAO 泛型基類"""def __class_getitem__(cls, __item):print('__class_getitem__')return _new_cls_with_grm_generic_args(cls, __item)def __init__(self):only_one_arg = getattr(self, "_grm_generic_arg", None)if not only_one_arg:raise TypeError("ModelT must be assigned")if isinstance(only_one_arg, UnionType):raise TypeError("ModelT must not be a UnionType")self.model: Type[ModelT] = only_one_argclass Item(BaseDAO[str]):def __init__(self):super().__init__()item = Item()
print(item.model) # 輸出: <class 'str'># 對一個類使用方括號,首先檢查其元類是否定義 __getitem__ 方法,有則調用,無則檢查該類是否定義 __class_getitem__ 方法,有則調用,無則報錯
方法二:
from types import UnionType
from typing import Generic, TypeVar, get_origin, get_args, TypeModelT = TypeVar('ModelT')class BaseDAO(Generic[ModelT]):"""DAO 泛型基類1. 一個類必須繼承一個 BaseDAO 或者 BaseDAO 子類,不能繼承多個BaseDAO 或者 BaseDAO 子類2. 泛型必須傳入一個類型,通常為 SQLAlchemy 中模型,不可為 types.UnionType"""def __init__(self):orig_bases = getattr(self.__class__, '__orig_bases__', None)if not orig_bases:raise TypeError("__orig_bases__ is empty")target_bases = [c for c in orig_bases if issubclass(get_origin(c), BaseDAO)]if len(target_bases) != 1:raise TypeError(f"{self.__class__} must extend a BaseDAO or BaseDAO's subclass")args = get_args(target_bases[0])if not len(args) == 1:raise RuntimeError("ModelT must be assigned")only_one_arg = args[0]if isinstance(only_one_arg, UnionType):raise TypeError("ModelT must not be a UnionType")self.model: Type[ModelT] = only_one_argclass Item(BaseDAO[str]):...item = Item()
print(item.model)