我有一个被声明为泛型的父类、一个抽象子类和该子类的具体实现,它声明了泛型类型:
MyType = TypeVar('MyType')
class A(Generic[MyType]):
a: MyType
class B(Generic[MyType], A[MyType]):
pass
class C(B[int]):
pass
但这并没有将泛型声明从 C 转发到 A,因此 a 的类型不是 int。有正确的方法吗?尝试搜索 SO 和 python 文档但找不到任何东西。
在
A
上,您有一个类变量,因此它在该类的所有实例之间共享。如果您尝试输入提示,则每当您创建 A
的新子类时都会发生冲突。
例如
a
这里有什么类型:
class A(Generic[MyType]):
a: MyType
class A1(A[str]):
pass
class A2(A[int]):
pass
如果你想在
A
上表示一个成员变量,那么你可以这样做:
class A(Generic[MyType]):
def __init__(self, a: MyType):
self.val = a
class B(Generic[MyType], A[MyType]):
def __init__(self, b: MyType):
A.__init__(self, b)
class C(B[int]):
def __init__(self, c: int):
B.__init__(self, c)
class D(B[str]):
def __init__(self, d: str):
B.__init__(self, d)
这里我们有两个类
C
和 D which both have different generics
intand
str`,并且类型提示起作用,因为我们正在创建具有不同泛型的子类。
希望 6 个月后这可能会有所帮助:)
@henry-b 接受的答案在几点上是错误的。
a
不是 类变量,因为它没有分配给任何东西。它仅添加到A.__annotations__
。A1.a
和 A2.a
(以及第二个示例中的类似 C.a
和 D.a
)已经具有不同的类型。没什么可担心的。B
没有“转发”泛型声明,但即使对于直接的具体子类来说也是如此:class A(Generic[T]):
a: T
class C(A[int]):
pass
inspect.get_annotations(A) # {'a': ~T}
inspect.get_annotations(B) # {}
分解已接受答案中的解决方案,它做了两件事:
val
(为了与原始问题保持一致,我将其称为a
)__init__
函数能够通过实现类型进行“检查”。我们可以用更简洁的方式来写:
# This is a pure abstract class, so it shouldn't have __init__
class A(Generic[T]):
a: T
class B(Generic[T], A[T]):
def __init__(self, a: T):
self.a = a
class C(B[int]):
def __init__(self, a: int):
super().__init__(a)
class D(B[str]):
pass
# As mentioned above, if we inspect `C.__init__` we get the impl type,
# while for `D.__init__` it's still the generic type:
inspect.get_annotations(C.__init__) # {'a': <class 'int'>}
inspect.get_annotations(D.__init__) # {'a': ~T}
接受的答案所做的不所做的就是它声称要做的:将类型注释从具体类“转发”到抽象祖先类(即回答问题)。实例变量
a
仍然是泛型类型:
C(1).__annotations__ # {'a': ~T}
D('foo').__annotations__ # {'a': ~T}
无论如何,这并不重要; PyType 可以通过任何一种方式捕获无效类型:
c = C(1) # fine
d = D('foo') # fine
c = C('foo') # pytype throws 'wrong-arg-types'
d = D(1) # pytype throws 'wrong-arg-types'
如果您确实希望具体类具有可检查的实例属性类型,则必须再次对其进行注释:
class E(B[bool]):
a: bool
def __init__(self, a: bool):
super().__init__(a)