Subscripted generics报错排查:向Restaurant添加Menu时遇类型检查问题
解决“Subscripted generics cannot be used with class and instance checks”错误
错误根源
你遇到的错误来自EnforcedTypeClass中的类型校验逻辑:Python的isinstance()函数不支持直接检查下标化的泛型类型(比如List[Section]、Union[Menu, None])。这类泛型在运行时无法直接作为类型参数传入isinstance,导致报错。
修复步骤及代码调整
1. 重构类型校验逻辑
修改EnforcedTypeClass,用typing模块的get_origin和get_args工具解析泛型类型,分别处理Union、List等泛型场景:
from typing import get_origin, get_args, Union, List from dataclasses import dataclass @dataclass() class EnforcedTypeClass: def __post_init__(self): for name, field_type in self.__annotations__.items(): value = self.__dict__[name] # 处理允许为None的字段 if value is None: origin = get_origin(field_type) if origin is Union and type(None) in get_args(field_type): continue raise TypeError(f"字段`{name}`不能为None,预期类型: {field_type}") # 解析泛型的原始类型(比如List的原始类型是list) origin_type = get_origin(field_type) or field_type # 处理Union类型:只要匹配任意一个分支即可 if origin_type is Union: valid_types = get_args(field_type) if not any(isinstance(value, t) for t in valid_types): current_type = type(value) raise TypeError(f"字段`{name}`赋值类型为{current_type},预期类型: {field_type}") continue # 处理List类型:先检查整体是列表,再检查每个元素的类型 if origin_type is List: item_type = get_args(field_type)[0] if not isinstance(value, list): current_type = type(value) raise TypeError(f"字段`{name}`赋值类型为{current_type},预期类型: {field_type}") for idx, item in enumerate(value): if not isinstance(item, item_type): item_current_type = type(item) raise TypeError(f"字段`{name}`中第{idx}个元素类型为{item_current_type},预期类型: {item_type}") continue # 普通类型的直接检查 if not isinstance(value, origin_type): current_type = type(value) raise TypeError(f"字段`{name}`赋值类型为{current_type},预期类型: {field_type}")
2. 修正update_one语法错误
你的路由代码中update_one的参数格式错误,外层多了一对大括号,正确写法是:
# 修正后 result = restaurants_collection.update_one( {"_id": restaurant_id}, {"$push": {"menu": menu}} )
3. 修正menu_id获取逻辑
update_one的返回结果没有inserted_id属性(这是insert_one的属性),如果需要给新增菜单生成ID,可手动用ObjectId创建:
from bson.objectid import ObjectId # 在创建menu字典后添加 menu = Menu(**data).as_dict() menu["_id"] = ObjectId() # 手动生成唯一ID # 后续返回时直接使用这个ID return make_response({ 'message': '新菜单已成功存入数据库', 'data': {"id": menu["_id"], **menu}}), 200
4. 修正Restaurant类的menu类型定义
你用$push往menu字段添加元素,说明数据库中menu是数组类型,但原Restaurant类中menu定义为Union[Menu, None],类型不匹配,需修改为列表类型:
from dataclasses import dataclass, field @dataclass() class Restaurant(EnforcedTypeClass): name : str address : str phone_number : str email : str hours_of_operation : str menu : List[Menu] = field(default_factory=list) # 改为列表,默认空列表 def as_dict(self): return { 'name' : self.name, 'address' : self.address, 'phone_number' : self.phone_number, 'email' : self.email, 'hours_of_operation' : self.hours_of_operation, 'menu' : [item.as_dict() for item in self.menu] # 对象列表转字典列表 }
额外注意事项
- 确保
Section类已正确定义,若需要类型校验,也应继承EnforcedTypeClass - 传入的
data中sections字段必须是符合Section类型要求的字典列表,否则会触发类型校验错误
内容的提问来源于stack exchange,提问作者itskashac
相关产品推荐
相关产品推荐

