使用范型缩小子类参数类型
2024年10月18日大约 4 分钟
在面向对象编程中,基类通常定义了一般性的行为,而子类则可以对这些行为进行扩展或细化。利用范型(Generic) ,我们可以编写更加灵活、可重用的代码。在这种情况下,子类不仅可以继承基类的方法,还可以对方法的参数类型进行进一步限制或缩小,从而提高代码的安全性和可读性。
本文将通过一个具体的代码示例,展示如何在Python中使用范型,并通过子类缩小父类方法参数的类型。
示例
范型是一种设计模式,可以使类或函数的类型参数化。通过引入类型参数,我们可以在不同的上下文中复用相同的代码,同时保持类型安全。typing.Generic
和TypeVar
是Python中实现范型的核心。
让我们看一个范型和子类缩小类型的具体例子:
from typing import Generic, TypeVar
from pydantic import BaseModel
import abc
class BaseParams(BaseModel):
action: str
class CreateParams(BaseParams):
name: str
node_ids: list[int]
class ExpandParams(BaseParams):
cluster_id: int
node_ids: list[int]
TParams = TypeVar("TParams", bound=BaseParams)
class BaseAction(Generic[TParams]):
def __init__(self, params: TParams):
self.params = params
self.action = params.action
@abc.abstractmethod
def run(self):
raise NotImplementedError
class CreateAction(BaseAction[CreateParams]):
def __init__(self, params: CreateParams):
super().__init__(params)
self.name: str = params.name
self.node_ids: list[int] = params.node_ids
self.action: str = params.action
def run(self) -> None:
print(
"{} cluster {}, use nodes: {}".format(self.action, self.name, self.node_ids)
)
return None
class ExpandAction(BaseAction[ExpandParams]):
def __init__(self, params: ExpandParams):
super().__init__(params)
self.cluster_id: int = params.cluster_id
self.node_ids: list[int] = params.node_ids
self.action: str = params.action
def run(self) -> None:
print(
"{} cluster {}, use nodes: {}".format(
self.action, self.cluster_id, self.node_ids
)
)
return None
在上面的代码中,我们定义了一个基类 BaseAction
,它使用了一个范型 TParams
,其中 TParams
被限定为 BaseParams
或其子类。这意味着任何继承 BaseAction
的子类都必须使用 BaseParams
的子类作为参数类型。
解析
- **定义基础参数类:**
BaseParams
是所有参数的基类,包含了一个通用的action
字段。它的两个子类CreateParams
和ExpandParams
分别添加了更多特定的字段,如name
、node_ids
和cluster_id
。 - **范型与基类的结合:**
BaseAction
类被定义为一个范型类,接受一个类型参数TParams
,并在构造函数中将params
的类型设为TParams
。这使得BaseAction
具有了更大的灵活性,它可以处理不同类型的参数。 - 子类缩小参数类型: 我们创建了两个子类
CreateAction
和ExpandAction
,它们分别继承自BaseAction
,并且将TParams
限制为CreateParams
和ExpandParams
。在各自的run
方法中,子类进一步细化了参数,如CreateAction
需要处理name
,而ExpandAction
则处理cluster_id
。
优势
- 提高代码的安全性:
通过子类缩小参数类型,我们可以确保在每个子类中,方法run
只能接受与该子类对应的参数类型。这种类型检查机制有助于在编译时发现潜在的错误。 - 增强可读性和可维护性:
子类明确了各自的参数要求,不同的业务逻辑由各自的子类独立管理。这样,代码更容易理解和维护。 - 重用代码:
BaseAction
实现了范型,允许子类在保持通用行为(如构造函数)的同时,定义各自特定的逻辑(如run
方法)。这避免了重复代码,同时又能根据需要定制行为。
结果
当我们运行上述代码时:
if __name__ == "__main__":
create_params = CreateParams(action="create", name="test", node_ids=[1, 2, 3])
create_action = CreateAction(create_params)
create_action.run()
expand_params = ExpandParams(action="expand", cluster_id=1, node_ids=[1, 2, 4])
expand_action = ExpandAction(expand_params)
expand_action.run()
输出将如下所示:
create cluster test, use nodes: [1, 2, 3]
expand cluster 1, use nodes: [1, 2, 4]
可以看到,CreateAction
和 ExpandAction
使用了各自特定的参数,并且 run
方法按照各自的逻辑生成了对应的输出。
总结
通过结合范型与继承,Python 提供了一种优雅的方式来缩小子类方法的参数类型。这种设计模式不仅提高了代码的类型安全性,还能增强代码的可读性和重用性。对于需要处理不同参数类型的复杂系统来说,使用范型可以帮助我们编写更加灵活、健壮的代码。
这种模式在现实应用中非常常见,尤其是在开发涉及多个业务场景的应用时,每个场景可能都有自己的特定参数和处理逻辑,但它们共享相似的操作。通过范型和子类,开发者可以简化代码结构,提高开发效率。