Python 中的受限泛型类型提示

Restricted generic type hint in Python

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

class A:
    ...

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

class C:
    ...

def factory(cls):
    return cls()

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

尝试 1

使用 typing 模块中的 Type

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!

这个正确地检测到 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

很好推断,factory(B).f()可以,而factory(A).f()则不行。但是,泛型没有限制,即。 factory(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

这看起来很有希望,至少 PyCharm 可以正确处理所有情况。但对于实际使用来说,这个解决方案实际上是最糟糕的 - A single constraint is not allowed 错误出现,但尚不清楚原因。在 PEP 484 中,只有简短的一行 'There should be at least two constraints, if any; specifying a single constraint is disallowed.'

有什么好的解决办法吗?我只有添加 'dummy' class _AA 的空白子 class,并将其作为另一个约束来拥有这样的东西。

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

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

提前致谢!

使用 TypeVarbound 似乎可行:

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"

The documentation about TypeVar 说:

...a type variable may specify an upper bound using bound=<type>. This means that an actual type substituted (explicitly or implicitly) for the type variable must be a subclass of the boundary type

这看起来和你想要的完全一样,所以你可以写:

T = TypeVar('T', bound=A)