8种参数组合的Field函数重载遇MyPy报错,求问题排查
问题背景
我尝试重写Pydantic和SQLModel的Field函数,用3个布尔参数区分8种参数组合,规则如下:
- 若
is_table_field == True,返回SQLModel的FieldInfo,否则返回Pydantic的FieldInfo - 若
is_state_field == True,必须指定additional_state_description参数;为False则不可指定 - 若
is_tag_field == True,必须指定additional_tag_description参数;为False则不可指定
现有代码
from typing import Literal, overload from pydantic.fields import FieldInfo as PydanticFieldInfo from sqlmodel.main import FieldInfo as SQLModelFieldInfo # TTT @overload def Field( description: str, is_table_field: Literal[True], is_state_field: Literal[True], is_tag_field: Literal[True], additional_state_description: str, additional_tag_description: str, ) -> SQLModelFieldInfo: ... # TTF @overload def Field( description: str, is_table_field: Literal[True], is_state_field: Literal[True], additional_state_description: str ) -> SQLModelFieldInfo: ... # TFT @overload def Field( description: str, is_table_field: Literal[True], is_tag_field: Literal[True], additional_tag_description: str ) -> SQLModelFieldInfo: ... # TFF @overload def Field(description: str, is_table_field: Literal[True]) -> SQLModelFieldInfo: ... # FTT @overload def Field( description: str, is_state_field: Literal[True], is_tag_field: Literal[True], additional_state_description: str, additional_tag_description: str, ) -> PydanticFieldInfo: ... # FTF @overload def Field(description: str, is_state_field: Literal[True], additional_state_description: str) -> PydanticFieldInfo: ... # FFT @overload def Field(description: str, is_tag_field: Literal[True], additional_tag_description: str) -> PydanticFieldInfo: ... # FFF @overload def Field(description: str) -> PydanticFieldInfo: ... def Field( description: str, is_table_field: bool = False, is_state_field: bool = False, is_tag_field: bool = False, additional_state_description: str | None = None, additional_tag_description: str | None = None, ) -> PydanticFieldInfo | SQLModelFieldInfo: if is_table_field: # TODO: implement return SQLModelFieldInfo() # TODO: implement return PydanticFieldInfo()
MyPy报错信息
- 重载函数实现不接受签名2的所有可能参数
- 重载函数实现不接受签名3的所有可能参数
- 重载函数实现不接受签名5的所有可能参数
- 重载函数实现不接受签名6的所有可能参数
- 重载函数实现不接受签名7的所有可能参数
问题原因与解决方法
核心问题
- 参数顺序不匹配:重载签名的参数顺序和实现函数的参数顺序不一致,导致调用重载时参数被错误分配。比如TTF重载中的
additional_state_description是第四个参数,但实现中第四个参数是is_tag_field,调用时会把additional_state_description的值传给is_tag_field,而additional_state_description会使用默认值None,违反了重载中必须传递该参数的约定。 - 重载签名不完整:部分重载未明确指定未使用的布尔参数的默认值,MyPy无法确认这些参数会被设为符合要求的默认值,因此判定签名不兼容。
- 缺少运行时校验:实现函数未校验规则2和3的参数约束,无法在运行时保证参数符合约定。
修正后的代码
from typing import Literal, overload from pydantic.fields import FieldInfo as PydanticFieldInfo from sqlmodel.main import FieldInfo as SQLModelFieldInfo # TTT @overload def Field( description: str, is_table_field: Literal[True], is_state_field: Literal[True], is_tag_field: Literal[True], additional_state_description: str, additional_tag_description: str, ) -> SQLModelFieldInfo: ... # TTF @overload def Field( description: str, is_table_field: Literal[True], is_state_field: Literal[True], additional_state_description: str, is_tag_field: Literal[False] = False, ) -> SQLModelFieldInfo: ... # TFT @overload def Field( description: str, is_table_field: Literal[True], is_tag_field: Literal[True], additional_tag_description: str, is_state_field: Literal[False] = False, ) -> SQLModelFieldInfo: ... # TFF @overload def Field( description: str, is_table_field: Literal[True], is_state_field: Literal[False] = False, is_tag_field: Literal[False] = False, ) -> SQLModelFieldInfo: ... # FTT @overload def Field( description: str, is_state_field: Literal[True], is_tag_field: Literal[True], additional_state_description: str, additional_tag_description: str, is_table_field: Literal[False] = False, ) -> PydanticFieldInfo: ... # FTF @overload def Field( description: str, is_state_field: Literal[True], additional_state_description: str, is_tag_field: Literal[False] = False, is_table_field: Literal[False] = False, ) -> PydanticFieldInfo: ... # FFT @overload def Field( description: str, is_tag_field: Literal[True], additional_tag_description: str, is_state_field: Literal[False] = False, is_table_field: Literal[False] = False, ) -> PydanticFieldInfo: ... # FFF @overload def Field( description: str, is_table_field: Literal[False] = False, is_state_field: Literal[False] = False, is_tag_field: Literal[False] = False, ) -> PydanticFieldInfo: ... def Field( description: str, is_table_field: bool = False, is_state_field: bool = False, additional_state_description: str | None = None, is_tag_field: bool = False, additional_tag_description: str | None = None, ) -> PydanticFieldInfo | SQLModelFieldInfo: # 校验规则2:is_state_field为True时必须传值,否则不能传 if is_state_field: if additional_state_description is None: raise ValueError("当is_state_field为True时,必须指定additional_state_description") else: if additional_state_description is not None: raise ValueError("当is_state_field为False时,不可指定additional_state_description") # 校验规则3:is_tag_field为True时必须传值,否则不能传 if is_tag_field: if additional_tag_description is None: raise ValueError("当is_tag_field为True时,必须指定additional_tag_description") else: if additional_tag_description is not None: raise ValueError("当is_tag_field为False时,不可指定additional_tag_description") # 拼接完整描述并返回对应FieldInfo full_description = description if is_state_field: full_description += f" | {additional_state_description}" if is_tag_field: full_description += f" | {additional_tag_description}" if is_table_field: return SQLModelFieldInfo(description=full_description) return PydanticFieldInfo(description=full_description)
修正说明
- 调整参数顺序:将
additional_state_description放在is_state_field之后,additional_tag_description放在is_tag_field之后,让重载与实现的参数顺序匹配,避免位置参数分配错误。 - 完善重载签名:在重载中明确添加未使用布尔参数的默认值(如
is_tag_field: Literal[False] = False),让MyPy确认参数符合约束,消除签名不兼容错误。 - 添加运行时校验:实现规则2和3的校验逻辑,确保运行时参数符合约定。
内容的提问来源于stack exchange,提问作者KOB
相关产品推荐
相关产品推荐

