Python mypy:如何定义泛型子类

Python mypy:如何定义泛型子类,python,types,mypy,Python,Types,Mypy,我有一个子类queue.queue如下: class SetQueue(queue.Queue): """Queue which will allow a given object to be put once only. Objects are considered identical if hash(object) are identical. """ def __init__(self, maxsize=0): """Initialise

我有一个子类
queue.queue
如下:

class SetQueue(queue.Queue):
    """Queue which will allow a given object to be put once only.

    Objects are considered identical if hash(object) are identical.
    """

    def __init__(self, maxsize=0):
        """Initialise queue with maximum number of items.

        0 for infinite queue
        """
        super().__init__(maxsize)
        self.all_items = set()

    def _put(self):
        if item not in self.all_items:
            super()._put(item)
            self.all_items.add(item)
我正在尝试使用静态类型检查。在这种情况下,SetQueue应该采用通用对象T。这是我迄今为止的尝试:

from typing import Generic, Iterable, Set, TypeVar

# Type for mypy generics
T = TypeVar('T')

class SetQueue(queue.Queue):
    """Queue which will allow a given object to be put once only.

    Objects are considered identical if hash(object) are identical.
    """

    def __init__(self, maxsize: int=0) -> None:
        """Initialise queue with maximum number of items.

        0 for infinite queue
        """
        super().__init__(maxsize)
        self.all_items = set()  # type: Set[T]

    def _put(self, item: T) -> None:
        if item not in self.all_items:
            super()._put(item)
            self.all_items.add(item)
mypy在类定义行上抛出一条警告,说“缺少泛型类型的类型参数”

我想我需要一个
Generic[T]
,但我所做的每一次尝试都会抛出一个语法错误。文档中的所有示例都显示了从
Generic[T]
生成子类,但没有从任何其他对象生成子类


有人知道如何定义SetQueue的泛型类型吗?

这里的问题是
queue.queue
实际上不是从
typing.generic
继承的,但是。在stdlib完全接受
输入之前,这是一个必要的缺点。因此,实际的
queue.queue
不具有在运行时为泛型类提供
\uu getitem\uuuuuuuuuuuuuu
功能的
typing.genericta
元类:

from typing import Generic, Iterable, Set, TypeVar, TYPE_CHECKING
import queue

# Type for mypy generics
T = TypeVar('T')


class SetQueue(queue.Queue[T]):
    """Queue which will allow a given object to be put once only.

    Objects are considered identical if hash(object) are identical.
    """

    def __init__(self, maxsize: int=0) -> None:
        """Initialise queue with maximum number of items.

        0 for infinite queue
        """
        super().__init__(maxsize)
        self.all_items = set()  # type: Set[T]

    def _put(self, item: T) -> None:
        if item not in self.all_items:
            super()._put(item)
            self.all_items.add(item)


my_queue = queue.Queue()  # type: queue.Queue[int]
my_queue.put(1)
my_queue.put('foo')  # error

my_set_queue = SetQueue()  # type: SetQueue[int]
my_set_queue.put(1)
my_set_queue.put('foo')  # error
例如,此代码类型在mypy中检查为ok,但在运行时失败:

from typing import Generic, Iterable, Set, TypeVar, TYPE_CHECKING
import queue

# Type for mypy generics
T = TypeVar('T')


class SetQueue(queue.Queue[T]):
    """Queue which will allow a given object to be put once only.

    Objects are considered identical if hash(object) are identical.
    """

    def __init__(self, maxsize: int=0) -> None:
        """Initialise queue with maximum number of items.

        0 for infinite queue
        """
        super().__init__(maxsize)
        self.all_items = set()  # type: Set[T]

    def _put(self, item: T) -> None:
        if item not in self.all_items:
            super()._put(item)
            self.all_items.add(item)


my_queue = queue.Queue()  # type: queue.Queue[int]
my_queue.put(1)
my_queue.put('foo')  # error

my_set_queue = SetQueue()  # type: SetQueue[int]
my_set_queue.put(1)
my_set_queue.put('foo')  # error
引发的错误是
TypeError:“type”对象不可订阅
,这意味着不支持
queue.queue[T]
(即
queue.queue.\uu getitem.\uuu

下面是一个让它在运行时也能工作的技巧:

from typing import Generic, Iterable, Set, TypeVar, TYPE_CHECKING
import queue

# Type for mypy generics
T = TypeVar('T')

if TYPE_CHECKING:
    Queue = queue.Queue
else:
    class FakeGenericMeta(type):
        def __getitem__(self, item):
            return self

    class Queue(queue.Queue, metaclass=FakeGenericMeta):
        pass


class SetQueue(Queue[T]):
    """Queue which will allow a given object to be put once only.

    Objects are considered identical if hash(object) are identical.
    """

    def __init__(self, maxsize: int=0) -> None:
        """Initialise queue with maximum number of items.

        0 for infinite queue
        """
        super().__init__(maxsize)
        self.all_items = set()  # type: Set[T]

    def _put(self, item: T) -> None:
        if item not in self.all_items:
            super()._put(item)
            self.all_items.add(item)


my_queue = queue.Queue()  # type: queue.Queue[int]
my_queue.put(1)
my_queue.put('foo')  # error

my_set_queue = SetQueue()  # type: SetQueue[int]
my_set_queue.put(1)
my_set_queue.put('foo')  # error
可能有更好的方法来修补元类。我很想知道是否有人能想出一个更优雅的解决方案


编辑:我应该注意到多重继承不起作用,因为
类SetQueue(queue.queue,Generic[T])
未能将
SetQueue
T
队列相关联。queue
的组合与继承(“有一个“vs”是a”)在这里可以非常有用,因为您可以准确地指定您想要的类型,而不是依赖于预期父类中的类型状态(这可能不是很好)

下面是
SetQueue
(来自问题)的完整实现,它100%通过了
mypy--strict
,没有任何问题(或黑客行为)。为了简洁起见,我删去了文档字符串

从键入导入通用、TypeVar、Set、可选
导入队列
T=TypeVar('T')#SetQueue中项目类型的泛型
类SetQueue(泛型[T]):
def uuu init uuu(self,maxsize:int=0)->无:
self.\u队列:queue.queue[T]=queue.queue(maxsize)
self.all_项:Set[T]=Set()
定义放置(自身,项目:T)->无:
如果项目不在self.all_项目中:
self.\u queue.put(项目)
self.all_items.add(item)
#100%的“继承”方法(奇数格式是压缩传递样板)
def task_done(self)->None:返回self.\u queue.task_done()
def join(self)->None:返回self.\u queue.join()
def qsize(self)->int:return self.\u queue.qsize()
def empty(self)->bool:返回self.\u queue.empty()
def full(self)->bool:返回self.\u queue.full()
def put_nowait(self,item:T)->None:返回self.put(item)
def get_nowait(self)->T:返回self.get()
def get(self,block:bool=True,timeout:Optional[float]=None)->T:
返回self.\u queue.get(块,超时)
def put(自身,项:T,块:bool=True,超时:可选[float]=None)->None:
返回self.\u queue.put(项、块、超时)
尽管组合肯定比继承更详细(因为它需要定义所有的传递方法),但代码的清晰度可能更好。此外,您并不总是希望所有的父方法和组合都可以省略它们


这样的组合在今天尤其重要,因为python生态系统(包括python标准库)中键入的当前状态并不是100%令人敬畏。基本上有两个平行世界:1)实际代码和2)键入。尽管您可能从代码的角度对一个伟大的类进行子类化,但这并不一定等同于继承伟大的(甚至是函数式的)类型定义。组合可以避免这种挫折。

类SetQueue(queue.queue,Generic[T])有什么问题?
写了这个问题后,我想知道我们是否应该使用多重继承。这是在现有类(本身没有类型注释)的子类上实现泛型类型的推荐方法吗?