Python 中的受限泛型类型提示

Tom*_*ons 2 python typing type-hinting

我想创建一个像这里一样的工厂方法。

class A:
    ...

class B(A):
    def f(self):
        ...

class C:
    ...

def factory(cls):
    return cls()
Run Code Online (Sandbox Code Playgroud)

但我想添加一些类型提示,有两个要求:

  • A允许 的子类作为 的参数factory
  • B传递时,正确检测到factory(B)是 的实例B,即。factory(B).f()允许,而factory(A).f()不允许。

尝试1

Type从模块中使用typing

from typing import Type

class A:
    ...

class B(A):
    def f(self):
        ...

class C:
    ...

def factory(cls: Type[A]):
    return cls()

factory(A)  # Ok
factory(B)  # Ok
factory(C)  # Fail
factory(A).f()  # Fail
factory(B).f()  # Fail -- Wrong!
Run Code Online (Sandbox Code Playgroud)

这个正确地检测到C不应作为 的参数传递factory。然而,factory(B).f()类型检查器不允许。

尝试2

使用TypeVar

from typing import TypeVar, Type
T = TypeVar('T')

class A:
    ...

class B(A):
    def f(self):
        ...

class C:
    ...

def factory(cls: Type[T]) -> T:
    return cls()

factory(A)  # Ok
factory(B)  # Ok
factory(C)  # Ok -- Wrong!
factory(A).f()  # Fail
factory(B).f()  # Ok
Run Code Online (Sandbox Code Playgroud)

可以很好地推断,这factory(B).f()是可以的,而factory(A).f()不是。然而,泛型是没有限制的,即。工厂(C)也可以。

尝试3

添加约束到T.

from typing import TypeVar, Type

class A:
    ...

class B(A):
    def f(self):
        ...

class D(A):
    ...

class C:
    ...

T = TypeVar('T', A)
def factory(cls: Type[T]) -> T:
    return cls()


factory(A)  # Ok
factory(B)  # Ok
factory(C)  # Fail
factory(A).f()  # Fail
factory(B).f()  # Ok
Run Code Online (Sandbox Code Playgroud)

这看起来很有希望,至少 PyCharm 可以正确处理所有情况。但对于实际使用来说,这个解决方案实际上是最糟糕的 -A single constraint is not allowed尽管出现了错误,但尚不清楚原因。在 PEP 484 中,只有一行简短的内容:“应该至少有两个约束,如果有的话;不允许指定单个约束。”

有什么好的解决办法吗?我只有添加一个“虚拟”类_A( 的空白子类)之类的东西A,并将其作为另一个约束来拥有类似的东西。

_A = NewType('_A', A)
T = TypeVar('T', A, _A)
def factory(cls: Type[T]) -> T:
    return cls()
Run Code Online (Sandbox Code Playgroud)

但我真的不认为这是一个很好的解决方案。

先感谢您!

Sam*_*ord 5

将 aTypeVar与 a 一起使用bound似乎有效:

from typing import Type, TypeVar

class A:
    ...

class B(A):
    def f(self):
        ...

class C:
    ...

AnyA = TypeVar("AnyA", bound=A)

def factory(cls: Type[AnyA]) -> AnyA:
    return cls()


factory(A).f()  # error: "A" has no attribute "f"
factory(B).f()  # ok!
factory(C)      # error: Value of type variable "AnyA" of "factory" cannot be "C"
Run Code Online (Sandbox Code Playgroud)