You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优化菜单分类类体系中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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 05:35:18