英文:
How to annotate a user-defined collection in Python?
问题
我已经创建了一个集合类型:
from collections.abc import Sequence
class MyCollection(Sequence):
pass
我想要像这样注释我的类的对象:
obj: MyCollection[int] = MyCollection()
如何实现这个目标?
我写了:
obj: MyCollection[int] = MyCollection()
但 IntelliSense 只指定了 MyCollection
而没有指定 MyCollection[int]
。
英文:
I've created a collection type:
from collections.abc import Sequence
class MyCollection(Sequence):
pass
and I want to annotate object of my class like this:
obj: MyCollection[int] = MyCollection()
How to achieve this?
I wrote:
obj: MyCollection[int] = MyCollection()
But IntelliSense only specifies MyCollection
not MyCollection[int]
.
答案1
得分: 0
你可以使用 typing
,它提供了运行时支持类型提示。对于用户定义的通用集合,你可以使用 typing.Generic
和 typing.TypeVar
。
这里是一个示例:
from typing import TypeVar, Generic
from collections.abc import Sequence
T = TypeVar('T')
class MyCollection(Sequence, Generic[T]):
def __init__(self):
self.data = []
def __getitem__(self, index):
return self.data[index]
def __len__(self):
return len(self.data)
def add(self, item: T):
self.data.append(item)
obj: MyCollection[int] = MyCollection()
obj.add(1)
英文:
You could use typing
it provides runtime support for type hints, for user-defined generic collection, you can use typing.Generic
and typing.TypeVar
Here is an example:
from typing import TypeVar, Generic
from collections.abc import Sequence
T = TypeVar('T')
class MyCollection(Sequence, Generic[T]):
def __init__(self):
self.data = []
def __getitem__(self, index):
return self.data[index]
def __len__(self):
return len(self.data)
def add(self, item: T):
self.data.append(item)
obj: MyCollection[int] = MyCollection()
obj.add(1)
答案2
得分: 0
以下是您请求的翻译内容:
要了解用户定义的泛型类型,请阅读PEP 484的这一部分。
显然,您想要子类化Sequence
抽象基类。请注意,collections.abc.Sequence
已经是泛型的。这意味着要在子类中保留泛型性,您只需在继承期间将类型变量传递给Sequence
。
您只需要确保正确实现了__getitem__
的重载签名,因为它应该接受索引 和 切片,并分别返回序列项 或 子序列。
示例:
from __future__ import annotations
from collections.abc import Iterable, Sequence
from typing import TypeVar, overload
T = TypeVar("T")
class MyCollection(Sequence[T]):
def __init__(self, data: Iterable[T]) -> None:
self.items = tuple(data)
@overload
def __getitem__(self, index: int) -> T: ...
@overload
def __getitem__(self, index: slice) -> MyCollection[T]: ...
def __getitem__(self, index: int | slice) -> T | MyCollection[T]:
if isinstance(index, slice):
return MyCollection(self.items[index])
return self.items[index]
def __len__(self) -> int:
return len(self.items)
尝试在mypy中使用它(演示):
s = MyCollection([10, 20, 30])
reveal_type(s)
reveal_type(s[0])
reveal_type(s[0:])
reveal_type(iter(s))
输出将如下所示:
note: Revealed type is "MyCollection[builtins.int]"
note: Revealed type is "builtins.int"
note: Revealed type is "MyCollection[builtins.int]"
note: Revealed type is "typing.Iterator[builtins.int]"
请注意,类型在使用时正确推断,无需显式注释。甚至迭代器项目类型也被正确推断,因为Sequence[T]
被设置为从其__iter__
方法返回Iterator[T]
。
这意味着,当s
例如是MyCollection[int]
类型时,您执行for item in s: ...
,静态类型检查器将推断item
的类型为int
。
英文:
To learn about user-defined generic types, read this section of PEP 484.
You apparently want to subclass the Sequence
abstract base class. Note that collections.abc.Sequence
is already generic. That means to retain genericity in a subclass, you simply need to pass a type variable to Sequence
during inheritance.
You just need to take care to implement the overloaded signatures of __getitem__
properly because it should accept indices and slices and return either a sequence item or a sub-sequence respectively.
Example:
from __future__ import annotations
from collections.abc import Iterable, Sequence
from typing import TypeVar, overload
T = TypeVar("T")
class MyCollection(Sequence[T]):
def __init__(self, data: Iterable[T]) -> None:
self.items = tuple(data)
@overload
def __getitem__(self, index: int) -> T: ...
@overload
def __getitem__(self, index: slice) -> MyCollection[T]: ...
def __getitem__(self, index: int | slice) -> T | MyCollection[T]:
if isinstance(index, slice):
return MyCollection(self.items[index])
return self.items[index]
def __len__(self) -> int:
return len(self.items)
Try that with mypy (playground):
s = MyCollection([10, 20, 30])
reveal_type(s)
reveal_type(s[0])
reveal_type(s[0:])
reveal_type(iter(s))
The output will be the following:
note: Revealed type is "MyCollection[builtins.int]"
note: Revealed type is "builtins.int"
note: Revealed type is "MyCollection[builtins.int]"
note: Revealed type is "typing.Iterator[builtins.int]"
Notice how the types are correctly inferred without the need for an explicit annotation during usage. Even the iterator item type is correctly inferred because Sequence[T]
is set up to return Iterator[T]
from its __iter__
method.
This means, when s
is e.g. of type MyCollection[int]
and you do for item in s: ...
and the static type checker will infer item
to be of type int
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论