如何用Python typing.TypeVar指定继承多个基类的类型约束?
问题
我们知道Python的TypeVar可以通过bound参数设置类型约束,示例如下:
from typing import TypeVar class A: pass T = TypeVar("T", bound=A) def foo(_: T) -> None: pass class B(A): pass class C: pass foo(B()) # OK foo(C()) # error: Value of type variable "T" of "foo" cannot be "C"
现在需要实现的是:如何表示任意同时继承自X和Y的类型T?
初步尝试通过创建一个同时继承X和Y的中间类XY,再将TypeVar的bound设为XY,但这种方案无效:
from typing import TypeVar class X: pass class Y: pass class XY(X, Y): pass T = TypeVar("T", bound=XY) def foo(_: T) -> None: pass class MyClass(X, Y): pass foo(MyClass()) # error: Value of type variable "T" of "foo" cannot be "MyClass"
因为MyClass并非XY的子类,mypy会正确报错。实际场景中,若存在多个基类(X1、X2……Xn),为每个组合创建中间类完全不现实。本质上想要实现类似如下虚构代码的效果:
T = TypeVar("T", bound=(X, Y)) # Not allowed def foo(x: T): assert isinstance(x, X) assert isinstance(x, Y)
解决方案
方法1:使用Protocol(推荐)
利用typing.Protocol定义一个同时兼容X和Y的协议,让TypeVar绑定这个协议。协议会自动检查类型是否实现了X和Y的所有成员,自然覆盖同时继承X和Y的情况:
from typing import TypeVar, Protocol class X: pass class Y: pass class XYProtocol(Protocol): __class__: type[X] & type[Y] # 若X/Y有特定方法/属性,可在此显式声明以增强检查 T = TypeVar("T", bound=XYProtocol) def foo(_: T) -> None: pass class MyClass(X, Y): pass foo(MyClass()) # OK
方法2:直接使用交集类型(Python 3.10+)
Python 3.10及以上版本支持用X & Y表示同时属于X和Y的类型,无需TypeVar就能实现约束,是最简单直接的方式:
class X: pass class Y: pass def foo(_: X & Y) -> None: pass class MyClass(X, Y): pass foo(MyClass()) # OK
方法3:结合TypeVar和交集类型
如果需要复用这个复合类型约束,可以将TypeVar的bound设为X & Y:
from typing import TypeVar class X: pass class Y: pass T = TypeVar("T", bound=X & Y) def foo(_: T) -> None: pass class MyClass(X, Y): pass foo(MyClass()) # OK
内容的提问来源于stack exchange,提问作者Bendik
相关产品推荐
相关产品推荐

