如何优化菜单分类类体系中get_entity方法的类型提示依赖?
菜单类体系的类型提示优化方案
问题背景
我设计了一套描述简易菜单的类体系:菜单包含分类,分类分为两种——带有子分类的分类和无子分类的分类。若分类带有子分类,产品会存储在子分类中;若分类无子分类,产品则直接存储在分类本身。我需要获取带有products属性的对象及其对应的名称(category_name或sub_category_name),但get_entity函数的类型提示存在大量重复依赖,希望优化。
现有代码
Category相关类
from dataclasses import dataclass, field from typing import Iterable, Union, ABC, abstractmethod @dataclass class Category(ABC): """表示通用分类。""" category_name: CategoryName @abstractmethod def get_entity(self) -> Iterable[Union[CategoryWithoutSubCategories, SubCategory]]: ... @dataclass class CategoryWithSubCategories(Category): """表示带有子分类的分类类型(如DRINKS、BURGERS等,DRINKS下包含SLUSHES、SOFT DRINKS等子分类;BURGERS下包含NATIONAL BURGERS等子分类)。""" sub_categories: list[SubCategory] = field(default_factory=list) def add_sub_category(self, sub_category: SubCategory) -> None: self.sub_categories.append(sub_category) def get_entity(self) -> Iterable[tuple[Union[CategoryWithoutSubCategories, SubCategory], SubCategoryName]]: for sub_category in self.sub_categories: yield sub_category, sub_category.sub_category_name @dataclass class CategoryWithoutSubCategories(Category): """表示无子分类的分类(如COMBOS、WACKY_PACK)。""" products: list[Product] = field(default_factory=list) def add_product(self, product: Product) -> None: self.products.append(product) def get_entity(self) -> Iterable[tuple[Union[CategoryWithoutSubCategories, SubCategory], CategoryName]]: yield self, self.category_name
SubCategory类
@dataclass class SubCategory: """表示通用子分类(如SLUSHES、SOFT DRINKS等)。""" sub_category_name: SubCategoryName products: list[Product] = field(default_factory=list) def add_product(self, product: Product) -> None: self.products.append(product)
优化方案
1. 定义复用的类型别名
将重复出现的复杂类型定义为别名,减少代码冗余:
from typing import TypeAlias # 统一指代所有包含products属性的容器 ProductContainer: TypeAlias = Union["CategoryWithoutSubCategories", SubCategory] # 统一指代名称类型 NameType: TypeAlias = Union[CategoryName, SubCategoryName] # 统一指代get_entity的返回类型 EntityResult: TypeAlias = Iterable[tuple[ProductContainer, NameType]]
2. 修正抽象方法的返回值契约
原抽象方法的返回值与子类实际返回值不一致(抽象方法返回容器对象,子类返回对象+名称的元组),违反了Liskov替换原则。统一将抽象方法的返回值改为EntityResult:
@dataclass class Category(ABC): """表示通用分类。""" category_name: CategoryName @abstractmethod def get_entity(self) -> EntityResult: ...
3. 简化子类的类型提示
子类实现get_entity时直接使用定义好的类型别名,无需重复写复杂的Union组合:
@dataclass class CategoryWithSubCategories(Category): # ... 其他代码不变 ... def get_entity(self) -> EntityResult: for sub_category in self.sub_categories: yield sub_category, sub_category.sub_category_name @dataclass class CategoryWithoutSubCategories(Category): # ... 其他代码不变 ... def get_entity(self) -> EntityResult: yield self, self.category_name
4. 可选:统一名称基类(如果适用)
如果CategoryName和SubCategoryName是同类型或可以继承自同一基类,比如:
class Name(str): pass class CategoryName(Name): pass class SubCategoryName(Name): pass
那么NameType可以直接定义为Name,进一步简化类型提示。
优化后的完整代码示例
from dataclasses import dataclass, field from typing import Iterable, Union, ABC, abstractmethod, TypeAlias # 定义类型别名 ProductContainer: TypeAlias = Union["CategoryWithoutSubCategories", SubCategory] NameType: TypeAlias = Union[CategoryName, SubCategoryName] EntityResult: TypeAlias = Iterable[tuple[ProductContainer, NameType]] # 假设的基础类型(根据实际情况调整) class CategoryName(str): pass class SubCategoryName(str): pass class Product: pass @dataclass class Category(ABC): """表示通用分类。""" category_name: CategoryName @abstractmethod def get_entity(self) -> EntityResult: ... @dataclass class CategoryWithSubCategories(Category): """表示带有子分类的分类类型(如DRINKS、BURGERS等,DRINKS下包含SLUSHES、SOFT DRINKS等子分类;BURGERS下包含NATIONAL BURGERS等子分类)。""" sub_categories: list[SubCategory] = field(default_factory=list) def add_sub_category(self, sub_category: SubCategory) -> None: self.sub_categories.append(sub_category) def get_entity(self) -> EntityResult: for sub_category in self.sub_categories: yield sub_category, sub_category.sub_category_name @dataclass class CategoryWithoutSubCategories(Category): """表示无子分类的分类(如COMBOS、WACKY_PACK)。""" products: list[Product] = field(default_factory=list) def add_product(self, product: Product) -> None: self.products.append(product) def get_entity(self) -> EntityResult: yield self, self.category_name @dataclass class SubCategory: """表示通用子分类(如SLUSHES、SOFT DRINKS等)。""" sub_category_name: SubCategoryName products: list[Product] = field(default_factory=list) def add_product(self, product: Product) -> None: self.products.append(product)
优化效果
- 减少了类型提示的重复代码,提升了代码可读性和维护性
- 修正了抽象方法与子类实现的返回值不一致问题,符合面向对象设计原则
- 类型别名的定义让类型意图更清晰,其他开发者能快速理解
ProductContainer和EntityResult的含义
内容的提问来源于stack exchange,提问作者user20291246
相关产品推荐
相关产品推荐

