将Python泛型传递给父类?

问题描述 投票:0回答:2

我有一个被声明为泛型的父类、一个抽象子类和该子类的具体实现,它声明了泛型类型:

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 文档但找不到任何东西。

python-3.x generics type-hinting
2个回答
1
投票

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 
int
and
str`,并且类型提示起作用,因为我们正在创建具有不同泛型的子类。

希望 6 个月后这可能会有所帮助:)


0
投票

@henry-b 接受的答案在几点上是错误的。

    示例中的
  1. a
    不是 类变量,因为它没有分配给任何东西。它仅添加到
    A.__annotations__
  2. 由于 (1),
    A1.a
    A2.a
    (以及第二个示例中的类似
    C.a
    D.a
    )已经具有不同的类型。没什么可担心的。
  3. 虽然原始示例中的抽象子类
    B
    没有“转发”泛型声明,但即使对于直接的具体子类来说也是如此:
class A(Generic[T]):
    a: T

class C(A[int]):
   pass

inspect.get_annotations(A)  # {'a': ~T}
inspect.get_annotations(B)  # {}

分解已接受答案中的解决方案,它做了两件事:

  1. 创建一个实例变量
    val
    (为了与原始问题保持一致,我将其称为
    a
  2. 使子类的
    __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)
© www.soinside.com 2019 - 2024. All rights reserved.