如何在泛型类型参数的类上更Pythonic地调用类方法?
问题描述
假设有如下代码:
from abc import ABC, abstractmethod from typing import Generic, Optional, Self, Type, TypeVar, get_args class EntityBase(ABC): @classmethod @abstractmethod def factory(cls) -> Self: pass class EntityA(EntityBase): @classmethod def factory(cls) -> Self: return cls() class EntityB(EntityBase): @classmethod def factory(cls) -> Self: return cls() T = TypeVar("T", bound=EntityBase) class EntityProcessorBase(ABC, Generic[T]): def get_all(self) -> Optional[EntityBase]: # 为简洁起见,省略了从外部源传入工厂的数据 return get_args(self.__class__.__orig_bases__[0])[0].factory() # type: ignore class EntityAProcessor(EntityProcessorBase[EntityA]): pass class EntityBProcessor(EntityProcessorBase[EntityB]): pass print(EntityAProcessor().get_all()) print(EntityBProcessor().get_all())
依赖__orig_bases__确实是在依赖CPython的实现细节(它首次出现于PEP-560),但暂时没有更好的思路。
即将推出的PEP-695提议在运行时提供__type_params__,该PEP说明:
该元组包含TypeVar、ParamSpec和TypeVarTuple实例。
我不确定如何通过TypeVar获取Type[T]——由于T被绑定到EntityBase,Type[T]肯定会拥有factory类方法。
根据此前StackOverflow的讨论,有几种方式可以重写代码实现相同功能,但我希望利用已有的T而不重复代码。
有没有比上述实现更Pythonic的方式?
更Pythonic的实现方案
依赖__orig_bases__确实不够稳健,这里提供两种更符合Python风格的实现方式,既避免访问内部属性,又能复用泛型参数T:
方案一:子类显式指定实体类(简洁直观)
修改基类,要求子类通过类属性绑定对应的实体类型,代码可读性高,完全不依赖内部实现:
from abc import ABC, abstractmethod from typing import Generic, Optional, Self, Type, TypeVar class EntityBase(ABC): @classmethod @abstractmethod def factory(cls) -> Self: pass class EntityA(EntityBase): @classmethod def factory(cls) -> Self: return cls() class EntityB(EntityBase): @classmethod def factory(cls) -> Self: return cls() T = TypeVar("T", bound=EntityBase) class EntityProcessorBase(ABC, Generic[T]): entity_class: Type[T] def get_all(self) -> Optional[T]: return self.entity_class.factory() class EntityAProcessor(EntityProcessorBase[EntityA]): entity_class = EntityA class EntityBProcessor(EntityProcessorBase[EntityB]): entity_class = EntityB print(EntityAProcessor().get_all()) print(EntityBProcessor().get_all())
方案二:利用__init_subclass__自动绑定
通过__init_subclass__钩子在子类继承时自动提取泛型参数,子类无需额外代码,同时避免直接依赖内部属性:
from abc import ABC, abstractmethod from typing import Generic, Optional, Self, Type, TypeVar, get_args class EntityBase(ABC): @classmethod @abstractmethod def factory(cls) -> Self: pass class EntityA(EntityBase): @classmethod def factory(cls) -> Self: return cls() class EntityB(EntityBase): @classmethod def factory(cls) -> Self: return cls() T = TypeVar("T", bound=EntityBase) class EntityProcessorBase(ABC, Generic[T]): entity_class: Type[T] def __init_subclass__(cls) -> None: # 从泛型基类中提取实体类型 generic_base = next(b for b in cls.__bases__ if hasattr(b, "__origin__")) cls.entity_class = get_args(generic_base)[0] def get_all(self) -> Optional[T]: return self.entity_class.factory() class EntityAProcessor(EntityProcessorBase[EntityA]): pass class EntityBProcessor(EntityProcessorBase[EntityB]): pass print(EntityAProcessor().get_all()) print(EntityBProcessor().get_all())
关于PEP-695的展望
当PEP-695正式落地后,可使用新泛型语法通过__type_params__直接获取泛型参数,代码会更简洁:
# 假设使用PEP-695的新泛型语法 from abc import ABC, abstractmethod from typing import Optional, Self, Type class EntityBase(ABC): @classmethod @abstractmethod def factory(cls) -> Self: pass class EntityA(EntityBase): @classmethod def factory(cls) -> Self: return cls() class EntityB(EntityBase): @classmethod def factory(cls) -> Self: return cls() class EntityProcessorBase[T: EntityBase](ABC): def get_all(self) -> Optional[T]: entity_class = self.__class__.__type_params__[0] return entity_class.factory() class EntityAProcessor(EntityProcessorBase[EntityA]): pass class EntityBProcessor(EntityProcessorBase[EntityB]): pass print(EntityAProcessor().get_all()) print(EntityBProcessor().get_all())
不过目前该语法尚未正式发布,需要等待对应Python版本支持。
内容的提问来源于stack exchange,提问作者Zoltán
相关产品推荐
相关产品推荐

