如何创建行为与T一致且运行时可区分的Python泛型类型A[T]?
实现行为与T一致但运行时可区分的泛型类型A[T]
方案一:动态继承子类(推荐)
通过动态创建继承自泛型基类和目标类型T的子类,既能让A[T]完全继承T的所有行为,又能在运行时区分实例类型,同时满足类型检查要求。
代码实现:
from typing import Generic, TypeVar, Type, cast import re T = TypeVar('T') class A(Generic[T]): # 缓存已创建的子类,避免重复生成 _cached_subclasses: dict[Type[T], Type['A[T]']] = {} def __class_getitem__(cls, item: Type[T]) -> Type[T]: # 若对应类型的子类未创建,则动态生成 if item not in cls._cached_subclasses: subclass_name = f"A[{item.__name__}]" # 创建继承自A和目标类型item的子类 subclass = cast(Type[T], type(subclass_name, (cls, item), {})) cls._cached_subclasses[item] = subclass return cls._cached_subclasses[item] def __init__(self, value: T) -> None: # 调用目标类型的构造函数完成初始化 super().__init__(value) # 测试类型检查与运行时行为 def f(s: A[str]) -> list[str]: # mypy会通过检查,因为A[str]被识别为str的子类型 return re.findall('foo|bar', s) # 运行时验证 a_str = A[str]("foobar") print(isinstance(a_str, str)) # 输出: True,行为与str一致 print(isinstance(a_str, A)) # 输出: True,属于A的实例 print(type(a_str)) # 输出: <class '__main__.A[str]'>,与原生str类型区分 print(type(a_str) is str) # 输出: False,确认类型不同
优缺点
- 优点:完全继承
T的所有方法和属性,无需手动代理;类型检查自动兼容T的场景;运行时清晰区分实例类型。 - 缺点:仅适用于可被继承的类型(绝大多数Python内置类型和自定义类都支持);动态生成子类需注意缓存避免重复开销。
方案二:包装器代理方案
通过包装T的实例,手动代理所有属性和方法,模拟T的行为,同时保留A的类型标识。
代码实现:
from typing import Generic, TypeVar, Any, cast import re T = TypeVar('T') class A(Generic[T]): def __init__(self, value: T) -> None: self._inner_value = value # 代理所有非特殊方法/属性到内部实例 def __getattr__(self, name: str) -> Any: return getattr(self._inner_value, name) # 实现特殊方法以兼容T的核心行为(按需添加) def __str__(self) -> str: return str(self._inner_value) def __repr__(self) -> str: return f"A({repr(self._inner_value)})" def __len__(self) -> int: return len(self._inner_value) def __getitem__(self, key: Any) -> Any: return self._inner_value[key] def __eq__(self, other: Any) -> bool: if isinstance(other, A): return self._inner_value == other._inner_value return self._inner_value == other # 测试类型检查与运行时行为 def f(s: A[str]) -> list[str]: # 使用cast告知mypy s可被当作str处理 return re.findall('foo|bar', cast(str, s)) # 运行时验证 a_str = A[str]("foobar") print(isinstance(a_str, A)) # 输出: True,属于A的实例 print(isinstance(a_str, str)) # 输出: False,但行为与str一致 print(str(a_str)) # 输出: foobar print(a_str.upper()) # 输出: FOOBAR(代理str的upper方法)
优缺点
- 优点:适用于所有类型,包括不可继承的类型;实现更灵活,可自定义代理逻辑。
- 缺点:需手动实现大量特殊方法(如
__add__、__mul__等)才能完全模拟T的行为;类型检查时需手动用cast兼容T的场景。
内容的提问来源于stack exchange,提问作者Knoep
相关产品推荐
相关产品推荐

