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

Python中如何对泛型序列参数进行类型收窄,限制为单一子类型?

如何为Python泛型函数添加类型注解,限制序列为单一BaseModel子类实例

问题背景

我需要为一个接收泛型序列的函数添加类型注解,要求序列中的元素必须是BaseModel的**单一子类(或自身)**实例,不能混合多个子类。比如下面的代码里,l1和l2是合法输入,但l3这种混合类型的序列应该触发pyright/mypy的静态检查错误,且不需要运行时校验。同时不能用逐个重载的方案,得支持BaseModel的任意子类。

原代码:

from collections.abc import Sequence
from pydantic import BaseModel, ConfigDict

class ExampleModel(BaseModel):
    id: int
    name: str
    model_config = ConfigDict(validate_default=True)

class NewModel(BaseModel):
    id: int
    name: str

def func[T: BaseModel](b: Sequence[T]) -> T:
    return b[0]

l1 = [ExampleModel(id=1, name="Test1"), ExampleModel(id=2, name="Test2")]
l2 = [NewModel(id=3, name="NewTest1"), NewModel(id=4, name="NewTest2")]
l3 = [ExampleModel(id=5, name="Test3"), NewModel(id=6, name="NewTest3")]

print(func(l1))
print(func(l2))
print(func(l3))

解决方案

方案1:Python 3.13+ 原生支持(推荐)

Python 3.13给TypeVar新增了constraint参数,可以自定义泛型参数的校验规则。我们可以用这个特性禁止T是联合类型:

from collections.abc import Sequence
from typing import TypeVar, Union
from pydantic import BaseModel

def is_non_union(t) -> bool:
    # 确保泛型参数不是联合类型
    return not isinstance(t, Union)

# 定义绑定到BaseModel且非联合类型的泛型变量
T = TypeVar('T', bound=BaseModel, constraint=is_non_union)

def func[T](b: Sequence[T]) -> T:
    return b[0]

这样一来,静态检查工具会识别l3的类型是Sequence[ExampleModel | NewModel],其中T是联合类型,违反约束,直接报错。

方案2:兼容Python 3.12及以下(pyright/mypy专属)

如果用的是Python 3.12或更早版本,可以通过静态检查工具的配置来实现需求:

针对pyright:

  1. 在项目根目录创建pyrightconfig.json,开启严格模式:
{
  "strict": true,
  "reportInvalidTypeVarUse": true
}
  1. 用Annotated标记泛型参数,明确禁止联合类型:
from collections.abc import Sequence
from typing import TypeVar, Annotated, Union
from pydantic import BaseModel

# 指定T必须是BaseModel的非联合子类
T = TypeVar('T', bound=Annotated[BaseModel, {"not": Union}])

def func[T](b: Sequence[T]) -> T:
    return b[0]

针对mypy:

开启严格模式后,mypy会对混合类型序列的调用给出提示。先在mypy.ini中配置:

[mypy]
strict = true
disallow_untyped_defs = true

然后保持原泛型函数写法,添加注释明确要求:

from collections.abc import Sequence
from typing import TypeVar
from pydantic import BaseModel

T = TypeVar('T', bound=BaseModel)

def func[T](b: Sequence[T]) -> T:
    """仅接受BaseModel单一子类的序列"""
    return b[0]

此时传入l3时,mypy会将T推断为联合类型,若要彻底禁止这种情况,可以结合typing_extensions的扩展特性或自定义插件,但一般严格模式下的提示已经满足需求。

效果验证

  • l1和l2会正常通过静态检查,因为它们的序列是单一子类的集合。
  • l3会触发静态检查错误,提示类型不兼容。

内容的提问来源于stack exchange,提问作者Frostyfeet909

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.01 13:12:37