Python如何基于已有类型生成全字段为Optional的新类型
自动生成全字段Optional类型的实现方案
你可以通过Python类型注解的内省能力,结合动态类创建逻辑实现需求,自动将原类所有非Optional字段转为Optional类型,且不会给已为Optional的字段生成嵌套Optional。
核心实现代码
首先编写通用的类生成函数,兼容普通类、dataclass、Pydantic模型等所有带类型注解的Python类:
from typing import Optional, Union, get_type_hints, get_origin, get_args import types def optionalize_class_fields(original_cls: type, suffix: str = "Query") -> type: new_cls_name = f"{original_cls.__name__}{suffix}" # 提取原类所有字段的类型注解 original_fields = get_type_hints(original_cls) new_annotations = {} for field_name, field_type in original_fields.items(): # 判断字段是否已经是Optional(即Union包含None类型) type_origin = get_origin(field_type) type_args = get_args(field_type) if type_origin is Union and type(None) in type_args: # 已是Optional,直接保留原类型 new_annotations[field_name] = field_type else: # 非Optional字段包装为Optional new_annotations[field_name] = Optional[field_type] # 动态创建新类,如需保留原类方法可保留继承,不需要可改为继承object new_cls = types.new_class(new_cls_name, (original_cls,)) new_cls.__annotations__ = new_annotations return new_cls
使用方式
直接调用函数即可生成你需要的SomeResourceQuery类,无需手动编写重复代码:
class SomeResource: id: int name: str # 已为Optional的字段会被原样保留 remark: Optional[str] = None # 一行生成目标类 SomeResourceQuery = optionalize_class_fields(SomeResource) # 验证类型注解结果 from typing import get_type_hints print(get_type_hints(SomeResourceQuery)) # 输出:{'id': typing.Optional[int], 'name': typing.Optional[str], 'remark': typing.Optional[str]}
仓储层的使用完全符合你的预期:
class SomeResourceRepository: def get_one_or_none(self, query: SomeResourceQuery) -> Optional[SomeResource]: # 业务查询逻辑,传None的字段代表不作为过滤条件 pass
版本兼容说明
如果你使用Python 3.10+的| None语法代替Optional写法,只需要修改类型判断逻辑,兼容新的联合类型写法即可:
# 放在类型判断的位置,替换原有if条件即可 import sys if sys.version_info >= (3, 10): import types as py_types is_optional = (type_origin is Union or type_origin is py_types.UnionType) and type(None) in type_args else: is_optional = type_origin is Union and type(None) in type_args if is_optional: new_annotations[field_name] = field_type else: new_annotations[field_name] = field_type | None # 也可继续用Optional[field_type]
内容的提问来源于stack exchange,提问作者stianlagstad
相关产品推荐
相关产品推荐

