我有一个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)
我正在尝试使用mypy进行静态类型检查。在这种情况下,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定义泛型类型?
答案 0 :(得分:4)
在这里,合成vs继承(“有一个”与“是一个”)非常有用,因为您可以准确指定要输入的 而不是依赖于输入状态预期的父类(可能不是很好)。
下面是SetQueue
的完整实现(根据问题),今天100%通过mypy --strict
而没有任何问题(或黑客)。为了简洁起见,我删除了文档字符串。
from typing import Generic, TypeVar, Set, Optional
import queue
T = TypeVar('T') # Generic for the item type in SetQueue
class SetQueue(Generic[T]):
def __init__(self, maxsize: int=0) -> None:
self._queue: queue.Queue[T] = queue.Queue(maxsize)
self.all_items: Set[T] = set()
def _put(self, item: T) -> None:
if item not in self.all_items:
self._queue.put(item)
self.all_items.add(item)
# 100% "inherited" methods (odd formatting is to condense passthrough boilerplate)
def task_done(self) -> None: return self._queue.task_done()
def join(self) -> None: return self._queue.join()
def qsize(self) -> int: return self._queue.qsize()
def empty(self) -> bool: return self._queue.empty()
def full(self) -> bool: return self._queue.full()
def put_nowait(self, item: T) -> None: return self.put(item)
def get_nowait(self) -> T: return self.get()
def get(self, block: bool = True, timeout: Optional[float] = None) -> T:
return self._queue.get(block, timeout)
def put(self, item: T, block: bool = True, timeout: Optional[float] = None) -> None:
return self._queue.put(item, block, timeout)
尽管组合肯定比继承更冗长(因为它需要定义所有直通方法),但是代码的清晰度可能会更好。另外,您并不总是希望所有的父方法和组合都可以忽略它们。
像这样构成现在在今天尤为重要,因为python生态系统(包括python标准库)的当前键入状态并不是100%出色的。基本上有两个平行的世界:1)实际代码,和2)输入。尽管从代码角度来看,您可能正在继承一个很棒的类,但这并不一定等于继承很棒的(甚至是功能性的)类型定义。组成可以避免这种挫败感。
答案 1 :(得分:3)
此处的问题是,queue.Queue
实际上并非从typing.Generic
继承,但typeshed stubs for it表示确实如此。这是一个必要的邪恶,直到stdlib完全购买typing
,如果有的话。因此,实际的queue.Queue
没有typing.GenericMeta
元类,它在运行时中为通用类提供了__getitem__
能力:
例如,此代码类型 - 在mypy中检查正常,但在运行时失败:
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__
)。
这里也是一个让它在运行时工作的黑客:
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
可能有更好的方法来修补元类。我很想知道是否有人想出更优雅的解决方案。
编辑:我应该注意多重继承不起作用,因为class SetQueue(queue.Queue, Generic[T])
无法将SetQueue
T
与queue.Queue
&#39; < / p>