Python 中 DAO 层使用泛型的探索
方法一:
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)