如何在实例化泛型类时复用当前类的泛型类型?
如何在泛型类中复用自身泛型类型实例化另一个泛型类?
当泛型类B被具体类型(如str)实例化后,要在B的方法里复用这个具体类型来实例化泛型类A,现有两种尝试都存在问题:
- 直接写
A[T]()会运行报错:object.__new__(X): X is not a type object (TypeVar),原因是类型变量T仅属于静态注解,运行时会被擦除,不是实际的类型对象 - 通过
self.__orig_class__.__args__[0]获取具体类型后实例化A[t](),虽然能正常运行,但会触发mypy的Variable "t" is not valid as a type错误,因为静态检查无法识别动态获取的类型变量
核心问题分析
Python的泛型类型变量(TypeVar)是为静态类型检查设计的,运行时会被擦除,所以直接用A[T]()无法得到有效类型;而__orig_class__是PEP 560引入的运行时属性,用于获取实例化时的原始泛型类型,但mypy的静态检查逻辑无法处理这种动态类型,因此会抛出错误。
解决方案1:优化泛型类设计,显式传递类型对象
修改A类,让它在初始化时接收具体的类型对象,彻底避免依赖泛型参数的运行时信息,同时保证静态检查能正常通过:
from typing import Generic, TypeVar, Type, cast from typing import get_args T = TypeVar('T') class A(Generic[T]): def __init__(self, t_type: Type[T]): self.t_type = t_type def get_t(self) -> T: # 直接用传入的类型创建实例(支持带构造参数的类型,可按需修改) return self.t_type() class B(Generic[T]): def get_t(self) -> T: # 运行时获取B实例对应的具体类型参数 orig_class = self.__orig_class__ t_type = get_args(orig_class)[0] # 用cast告知mypy该类型为Type[T],通过静态检查 t_type = cast(Type[T], t_type) # 实例化A时传入类型对象,无需依赖泛型参数语法 a = A(t_type) return a.get_t() if __name__ == '__main__': a = A[str](str) obj: str = a.get_t() print(type(obj)) # 输出:<class 'str'> b = B[str]() obj: str = b.get_t() print(type(obj)) # 输出:<class 'str'>
这种方案逻辑清晰,同时兼容运行时和静态检查,是更推荐的实现方式。
解决方案2:保留原泛型参数语法,兼容mypy检查
如果希望保留A类的原有设计,仅修改B类,可以通过cast和# type: ignore来绕过mypy的静态检查限制:
from typing import Generic, TypeVar, Type, cast from typing import get_args T = TypeVar('T') class A(Generic[T]): def get_t(self) -> T: t = self.__orig_class__.__args__[0] obj = t.__new__(t) obj.__init__() return obj class B(Generic[T]): def get_t(self) -> T: orig_class = self.__orig_class__ t_type = get_args(orig_class)[0] t_type = cast(Type[T], t_type) # 用type: ignore忽略mypy对动态类型参数的检查 a = A[t_type]() # type: ignore[valid-type] return a.get_t() if __name__ == '__main__': a = A[str]() obj: str = a.get_t() print(type(obj)) # 输出:<class 'str'> b = B[str]() obj: str = b.get_t() print(type(obj)) # 输出:<class 'str'>
这里用get_args替代直接索引__args__,代码更健壮;cast辅助mypy识别类型,# type: ignore跳过静态检查无法覆盖的动态类型部分。
注意事项
__orig_class__仅存在于泛型类的实例上,如果是继承泛型类的子类,可能需要额外处理类型参数的传递- 若目标类型需要构造参数,需在
get_t方法中补充对应的初始化逻辑(比如传入__init__所需的参数)
内容的提问来源于stack exchange,提问作者Jeppe
相关产品推荐
相关产品推荐

