通用(协议)class 参数的类型注释

Type annotation for generic (protocol) class argument

我正在尝试找出如何为应该是 class 实现通用协议的函数参数添加类型注释。

举个例子,假设我有一个看起来像这样的集合的协议:

from typing import (
    Protocol, TypeVar, Iterable
)

T = TypeVar('T', contravariant=True)


class Set(Protocol[T]):
    """A set of elements of type T."""

    def __init__(self, init: Iterable[T]) -> None:
        """Initialise set with init."""
        ...

    def __contains__(self, x: T) -> bool:
        """Test if x is in set."""
        ...

    def add(self, x: T) -> None:
        """Add x to the set."""
        ...

    def remove(self, x: T) -> None:
        """Remove x from the set."""
        ...

我有一个使用各种类型集合的算法,我想用集合实现对其进行参数化。为简单起见,我将在此函数中创建一个列表以用作示例:

from typing import Type

def foo(set_type: Type[Set]) -> None:
    """Do clever stuff."""
    x = list(range(10))
    s = set_type(x)
    ...

在这里,mypy 告诉我 Set 缺少一个类型参数,我认为这是正确的,但我不想给它一个,因为我打算使用 set_type 有不同的类型。

如果我给 Set 一个 TypeVar 而不是

def foo(set_type: Type[Set[T]]) -> None:
    """Do clever stuff"""
    x = list(range(10))
    s = set_type(x)
    ...

我得到的警告是 set_type() 得到了不兼容的类型,List[int] 而不是 Iterable[T],这也是正确的,但对我帮助不大。

有没有办法指定我的函数参数可以用作不同类型集合的通用构造函数?

Protocol 没有说明 __init__ 的签名,即使它是在 Protocol 上定义的。 Type 做类似的事情 - 即使 Set 不是 ProtocolType[Set] 也没有说明该类型的调用方式。

我最初建议使用 Callable[[Iterable[T]], Set[T]]。然而,这是有问题的,并且只因为我省略了通用参数,基本上使它成为 Any,如 this Github issue 中所讨论的那样。您可以改为使用(相当冗长的)协议。

class MkSet(Protocol):
    def __call__(self, it: Iterable[T]) -> Set[T]:
        ...

def foo(set_type: MkSet) -> None:
    ...