python中多重继承和泛型 作为模板让子类实现具体业务逻辑
示例代码:
T = TypeVar("T", bound="NoSQLBaseDocument")
# 与 MongoDB 数据库交互的基础文档类
class NoSQLBaseDocument(BaseModel, Generic[T], ABC):
id: UUID4 = Field(default_factory=uuid.uuid4)
def __eq__(self, value: object) -> bool:
if not isinstance(value, self.__class__):
return False
return self.id == value.id
def __hash__(self) -> int:
return hash(self.id)
这段代码结合使用了 Python 的泛型和多重继承:
第一行代码:泛型类型变量定义
T = TypeVar("T", bound="NoSQLBaseDocument")
通俗解释:
- TypeVar(“T”):表示我们定义了一个类型变量,名字叫 T,就好比给“类型”起了个名字,方便后续在代码中使用。
- bound=“NoSQLBaseDocument”:表示这个 T 只能替换为
NoSQLBaseDocument
或它的子类。就是说,如果你使用 T,那么它一定得是从 NoSQLBaseDocument 继承来的,不能乱用其他类型。
这里class NoSQLBaseDocument(BaseModel, Generic[T], ABC):
又继承了Generic[T]
是用到了自引用泛型,让类在定义时能够引用自己作为类型参数
举例说明:
- 假设你有一个类
ArticleDocument
,它继承自NoSQLBaseDocument
,那么 T 可以代表ArticleDocument
(因为ArticleDocument
是NoSQLBaseDocument
的子类)。 - 如果你试图让 T 代表一个数字(例如
int
),就会不符合要求,因为int
不是 NoSQLBaseDocument 或它的子类。
可以理解为:
“T 就像一个占位符,表示‘某种文档类型’,但这个文档类型必须是 NoSQLBaseDocument 或它的后代。”
第二行代码:定义基础文档类
class NoSQLBaseDocument(BaseModel, Generic[T], ABC):
id: UUID4 = Field(default_factory=uuid.uuid4)
这行代码同时用了多重继承和泛型,我们逐部分解释:
-
继承自 BaseModel
- 意思:这个类继承了 Pydantic 的 BaseModel,可以利用它提供的数据验证、序列化等功能。
- 举例:就像你有一个模具,保证所有“文档”数据符合一定的格式,比如字段类型和默认值。
-
继承自 Generic[T]
- 意思:这表示
NoSQLBaseDocument
是一个泛型类,它可以在定义中使用 T。这样,当其他类继承它时,类型检查器就知道它们返回的对象一定和 T 有关。 - 举例:假设你定义了一个
ArticleDocument
类继承自NoSQLBaseDocument
。此时,T 就自动代表ArticleDocument
。如果调用ArticleDocument
中的某个方法返回 T,那么类型检查器会认为返回的就是ArticleDocument
类型,而不是仅仅一个通用的文档。
- 意思:这表示
-
继承自 ABC (Abstract Base Class)
- 意思:这表示
NoSQLBaseDocument
是一个抽象类,不能直接创建实例,只能用来让其它类继承并实现具体功能。 - 举例:就像一个工厂设计图,你不能直接拿来用,必须先制造出具体的产品,比如
UserDocument
或ArticleDocument
。
- 意思:这表示
-
字段 id 的定义
id: UUID4 = Field(default_factory=uuid.uuid4)
- 意思:定义了一个名为
id
的字段,其类型为 UUID4,默认值是调用uuid.uuid4()
生成的一个随机唯一标识符。 - 举例说明:
- 如果你创建一个文档对象,例如:
而你没有给doc = ArticleDocument(...)
id
赋值,系统会自动生成一个 id,可能是"123e4567-e89b-12d3-a456-426614174000"
这样的字符串。 - 就好比你制作一个产品,每个产品都有一个自动生成的序列号,不需要你手动输入。
- 如果你创建一个文档对象,例如:
- 意思:定义了一个名为
综合举例
假设我们有两个具体文档类:
# 定义具体文档类1:文章
class ArticleDocument(NoSQLBaseDocument):
title: str
content: str
# 定义具体文档类2:用户
class UserDocument(NoSQLBaseDocument):
first_name: str
last_name: str
-
在这两个类中,由于它们都继承自
NoSQLBaseDocument
,因此自动拥有一个id
字段,且这个id
会自动生成一个唯一值。 -
当你调用
ArticleDocument
中的某个方法,类型检查器就知道这里的 T 实际上是ArticleDocument
。比如你写了一个get_or_create
类方法返回 T,那么在ArticleDocument.get_or_create(...)
调用时,返回的对象类型就是ArticleDocument
。
数值例子:
- 如果你创建了两个
ArticleDocument
实例:article1
自动生成id = "1111-2222-3333-4444"
article2
自动生成id = "5555-6666-7777-8888"
- 使用
Generic[T]
,当你调用ArticleDocument.get_or_create(title="Python教程")
,类型检查器会确认返回的是ArticleDocument
类型,且你可以安全地使用article1.title
等属性。
总的来说,这两行代码结合使用了 Python 的泛型和多重继承:
- 泛型(Generic[T]):使得类可以在方法返回值和类型提示中使用 T,从而保证具体子类类型的一致性。
- 多重继承:同时继承了 BaseModel(数据验证)、Generic[T](泛型支持)和 ABC(抽象基类),让这个基础文档类既能保证数据格式,又能作为模板让子类实现具体业务逻辑。