
mypy 在泛型函数中无法将 T: A | B | C 正确推断为具体单一类型,导致 elem.dup() 返回的 list[A] | list[B] | list[C] 无法赋值给 list[T];应改用元组形式的类型变量约束 T: (A, B, C),确保类型参数被精确绑定。
mypy 在泛型函数中无法将 `t: a | b | c` 正确推断为具体单一类型,导致 `elem.dup()` 返回的 `list[a] | list[b] | list[c]` 无法赋值给 `list[t]`;应改用元组形式的类型变量约束 `t: (a, b, c)`,确保类型参数被精确绑定。
在 Python 类型提示中,T: A | B | C 表示 联合类型上界(upper bound),即 T 可以是 A、B 或 C 的任意子类型(包括它们自身),但 mypy 不会将实际传入的实参类型“固定”为某个具体类型——它仅保证 T 属于该联合集,因此 elem.dup() 的返回类型被保守推断为 list[A] | list[B] | list[C],而 list[T] 是一个依赖于 T 的泛型类型,二者在类型系统中不兼容(list[A] | list[B] | list[C] 并非 list[T] 的子类型)。
正确做法是使用 约束类型变量(constrained type variable),语法为 T: (A, B, C)(注意是圆括号内的元组,而非竖线分隔的联合类型):
from typing import Self, reveal_type
class A:
def dup(self) -> list[Self]:
return [self, self]
class B:
def dup(self) -> list[Self]:
return [self, self]
class C:
def dup(self) -> list[Self]:
return [self, self]
def process[T: (A, B, C)](elem: T) -> list[T]:
reveal_type(elem.dup) # Revealed type is "def () -> builtins.list[T@process]"
duplicated: list[T] = elem.dup() # ✅ Now type-checks successfully
return duplicated
process(A()) # T bound to A → returns list[A]
process(B()) # T bound to B → returns list[B]
process(C()) # T bound to C → returns list[C]
✅ 关键区别总结:
| 写法 | 类型变量性质 | mypy 对 T 的处理 |
是否支持 elem.dup() → list[T] 赋值 |
|---|---|---|---|
T: A | B | C |
上界约束(upper bound) | 视 T 为可变的联合上界,不固定实参类型 |
❌ 失败:返回 list[A] \| list[B] \| list[C]
|
T: (A, B, C) |
约束列表(constrained type variable) | 在每次调用时将 T 精确绑定为 A、B 或 C 中的一个 |
✅ 成功:elem.dup() 被推断为 list[T]
|
⚠️ 注意事项:
-
T: (A, B, C)中的括号不可省略,且必须是字面量元组(不能是变量或表达式); - 所有约束类型需彼此无关(无继承关系),否则可能引发意外子类型行为;若存在继承(如
B继承A),建议显式建模为协议(Protocol)或使用TypeVar('T', bound=Base); -
Self在各dup方法中能正确与T对齐,得益于Self的“当前类实例类型”语义,配合约束类型变量可实现精准协变返回。
综上,这不是 mypy 的 bug,而是类型系统对联合上界与约束类型变量的严格区分——选择 T: (A, B, C) 才能实现预期的单态泛型行为。










