如何在Python中注释用户定义的集合?

huangapple go评论72阅读模式
英文:

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.Generictyping.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.

huangapple
  • 本文由 发表于 2023年5月13日 21:58:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/76243101.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定