mypy:如何定义通用子类 [英] mypy: how to define a generic subclass
问题描述
我有一个 queue.Queue
的子类,如下所示:
I have a subclass of queue.Queue
like so:
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)
我正在尝试使用 mypy 进行静态类型检查.在这种情况下,SetQueue 应该采用一个通用对象 T.这是我目前的尝试:
I am trying to use mypy for static type checking. In this case, the SetQueue should take a generic object T. This is my attempt so far:
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 在类定义行上发出警告,指出缺少泛型类型的类型参数".
mypy throws a warning on the class definition line saying "Missing type parameters for generic type".
我认为我需要在某处使用 Generic[T]
但我所做的每一次尝试都会引发语法错误.文档中的所有示例都显示了从 Generic[T]
的子类化,但不从任何其他对象子类化.
I think that I need a Generic[T]
somewhere but every attempt that I have made throws a syntax error. All of the examples in the docs show subclassing from Generic[T]
but don't subclass from any other object.
有谁知道如何定义 SetQueue 的泛型类型?
Does anyone know how to define the generic type for SetQueue?
推荐答案
这里的问题是 queue.Queue
实际上并不是从 typing.Generic
继承的,而是它的打字存根 表示确实如此.在 stdlib 完全接受 typing
(如果有的话)之前,这有点必要.因此,实际的 queue.Queue
没有 typing.GenericMeta
元类,它在运行时为泛型类提供了 __getitem__
能力:
The problem here is that queue.Queue
does not actually not inherit from typing.Generic
, but the typeshed stubs for it says that it does. This is a bit of a necessary evil until the stdlib fully buys into typing
, if ever. As a result, the actual queue.Queue
does not have the typing.GenericMeta
metaclass that gives generic classes their __getitem__
ability at runtime:
例如,此代码在 mypy 中类型检查正常,但在运行时失败:
For example, this code type-checks ok in mypy, but fails at runtime:
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' object is not subscriptable
,意思是queue.Queue[T]
(即queue.Queue.__getitem__
) 不受支持.
The error raised is TypeError: 'type' object is not subscriptable
, meaning that queue.Queue[T]
(i.e. queue.Queue.__getitem__
) is not supported.
这里有一个技巧可以让它在运行时也能工作:
Here's a hack to make it work at runtime as well:
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
可能有更好的方法在元类中打补丁.我很想知道是否有人提出了更优雅的解决方案.
There may be a better way to patch in the metaclass. I'm curious to know if anyone comes up with a more elegant solution.
我应该注意到多重继承不起作用,因为 class SetQueue(queue.Queue, Generic[T])
未能关联 SetQueue
的 T
到 queue.Queue
的
I should note that multiple inheritance did not work because class SetQueue(queue.Queue, Generic[T])
fails to relate SetQueue
's T
to queue.Queue
's
这篇关于mypy:如何定义通用子类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!