Python 类型提示,可索引对象
Python type-hinting, indexable object
我的函数需要接受一个对象,可以通过索引从中提取数据,即。 List
或定义了 __getitem__
方法的实例。
我可以使用哪种类型来提示此参数?
更新:
据我所知目前没有这样的类型,我试着自己做了一个:
class IndexableContainer(Generic[int, ReturnType]):
def __getitem__(self, key: int) -> ReturnType:
...
但我收到以下错误:
File "indexable_container.py", line 22, in IndexableContainer
class IndexableContainer(Generic[int, ReturnType]):
File ".../lib/python3.6/typing.py", line 682, in inner
return func(*args, **kwds)
File ".../lib/python3.6/typing.py", line 1112, in __getitem__
"Parameters to Generic[...] must all be type variables")
TypeError: Parameters to Generic[...] must all be type variables
我应该怎么做?
看来我们能得到的最接近的是:
Mapping[int, Any]
虽然不是我想要的,但也差不多了。
有几种不同的方法可以做到这一点。
如果您只使用自定义 类(您可以编写)作为可索引容器,您需要做的就是调整您的代码并删除 'int' 类型参数:
class IndexableContainer(Generic[ReturnType]):
def __getitem__(self, key: int) -> ReturnType:
...
class MyCustomContainer(IndexableContainer[ReturnType]):
def __getitem__(self, key: int) -> ReturnType:
# Implementation here
def requires_indexable_container(container: IndexableContainer[ReturnType]) -> ReturnType:
# Code using container here
问题当然是,如果您想将一个普通的旧列表传递给函数,您将无法这样做,因为列表不会子类化您的自定义类型。
我们也许可以通过巧妙地使用 @overload
装饰器和联合来对某些输入进行特殊处理,但是还有第二种方法,尽管是实验性的,称为 Protocols.
协议基本上可以让您使用类型提示以一种理智的方式表达 "duck typing":基本思想是我们可以调整 IndexableContainer 使其成为一个协议。现在,任何使用适当签名实现 __getitem__
方法的对象都被视为有效的 IndexableContainer,无论它们是否是该类型的子类。
唯一需要注意的是,协议目前处于实验阶段,并且 (afaik) 仅受 mypy 支持。计划最终将协议添加到通用 Python 生态系统中——具体提案请参阅 PEP 544——但我没有跟踪 discussion/don 不知道状态如何
无论如何,要使用协议,请使用 pip 安装 typing_extensions
模块。然后,您可以执行以下操作:
from typing_extensions import Protocol
# ...snip...
class IndexableContainer(Protocol, Generic[ReturnType]):
def __getitem__(self, key: int) -> ReturnType:
...
def requires_indexable_container_of_str(container: IndexableContainer[str]) -> None:
print(container[0] + "a")
a = ["a", "b", "c"]
b = {0: "foo", 2: "bar"}
c = "abc"
d = [1, 2, 3]
# Type-checks
requires_indexable_container_of_str(a)
requires_indexable_container_of_str(b)
requires_indexable_container_of_str(c)
# Doesn't type-check
requires_indexable_container_of_str(d)
suggests typing.Sequence
。
此类型同时支持 __getitem__
和 __len__
.
然而,鉴于它目前已被弃用,我想使用 collections.abc.Sequence
.
会更好
正如作者稍后在评论中提到的那样,he/she 实际上也需要 __delitem__
的东西,在这种情况下 collections.abc.MutableSequence
可能是最合适的(它是@Yuval 在评论中也提到了)。它支持所有 __getitem__
、__setitem__
、__delitem__
、__len__
和 insert
。
最终类型的用法示例(改编自):
from collections.abc import MutableSequence
def foo(bar: MutableSequence[Any]):
# bar is a mutable sequence of any objects
我的函数需要接受一个对象,可以通过索引从中提取数据,即。 List
或定义了 __getitem__
方法的实例。
我可以使用哪种类型来提示此参数?
更新: 据我所知目前没有这样的类型,我试着自己做了一个:
class IndexableContainer(Generic[int, ReturnType]):
def __getitem__(self, key: int) -> ReturnType:
...
但我收到以下错误:
File "indexable_container.py", line 22, in IndexableContainer
class IndexableContainer(Generic[int, ReturnType]):
File ".../lib/python3.6/typing.py", line 682, in inner
return func(*args, **kwds)
File ".../lib/python3.6/typing.py", line 1112, in __getitem__
"Parameters to Generic[...] must all be type variables")
TypeError: Parameters to Generic[...] must all be type variables
我应该怎么做?
看来我们能得到的最接近的是:
Mapping[int, Any]
虽然不是我想要的,但也差不多了。
有几种不同的方法可以做到这一点。
如果您只使用自定义 类(您可以编写)作为可索引容器,您需要做的就是调整您的代码并删除 'int' 类型参数:
class IndexableContainer(Generic[ReturnType]):
def __getitem__(self, key: int) -> ReturnType:
...
class MyCustomContainer(IndexableContainer[ReturnType]):
def __getitem__(self, key: int) -> ReturnType:
# Implementation here
def requires_indexable_container(container: IndexableContainer[ReturnType]) -> ReturnType:
# Code using container here
问题当然是,如果您想将一个普通的旧列表传递给函数,您将无法这样做,因为列表不会子类化您的自定义类型。
我们也许可以通过巧妙地使用 @overload
装饰器和联合来对某些输入进行特殊处理,但是还有第二种方法,尽管是实验性的,称为 Protocols.
协议基本上可以让您使用类型提示以一种理智的方式表达 "duck typing":基本思想是我们可以调整 IndexableContainer 使其成为一个协议。现在,任何使用适当签名实现 __getitem__
方法的对象都被视为有效的 IndexableContainer,无论它们是否是该类型的子类。
唯一需要注意的是,协议目前处于实验阶段,并且 (afaik) 仅受 mypy 支持。计划最终将协议添加到通用 Python 生态系统中——具体提案请参阅 PEP 544——但我没有跟踪 discussion/don 不知道状态如何
无论如何,要使用协议,请使用 pip 安装 typing_extensions
模块。然后,您可以执行以下操作:
from typing_extensions import Protocol
# ...snip...
class IndexableContainer(Protocol, Generic[ReturnType]):
def __getitem__(self, key: int) -> ReturnType:
...
def requires_indexable_container_of_str(container: IndexableContainer[str]) -> None:
print(container[0] + "a")
a = ["a", "b", "c"]
b = {0: "foo", 2: "bar"}
c = "abc"
d = [1, 2, 3]
# Type-checks
requires_indexable_container_of_str(a)
requires_indexable_container_of_str(b)
requires_indexable_container_of_str(c)
# Doesn't type-check
requires_indexable_container_of_str(d)
typing.Sequence
。
此类型同时支持 __getitem__
和 __len__
.
然而,鉴于它目前已被弃用,我想使用 collections.abc.Sequence
.
正如作者稍后在评论中提到的那样,he/she 实际上也需要 __delitem__
的东西,在这种情况下 collections.abc.MutableSequence
可能是最合适的(它是@Yuval 在评论中也提到了)。它支持所有 __getitem__
、__setitem__
、__delitem__
、__len__
和 insert
。
最终类型的用法示例(改编自
from collections.abc import MutableSequence
def foo(bar: MutableSequence[Any]):
# bar is a mutable sequence of any objects