Python类型提示,可索引对象

Python类型提示,可索引对象,python,type-hinting,Python,Type Hinting,我的函数需要接受一个对象,从中可以通过索引提取数据,即。列表或具有已定义的\uuuu getitem\uuuu方法的实例 我可以使用哪种类型来暗示此参数 更新: 据我所知,目前还没有这样的类型,我尝试自己制作一个: 类IndexableContainer(通用[int,ReturnType]): def\uu getitem\uuu(self,key:int)->返回类型: ... 但我得到了以下错误: 文件“indexable_container.py”,第22行,在IndexableCon

我的函数需要接受一个对象,从中可以通过索引提取数据,即。
列表
或具有已定义的
\uuuu getitem\uuuu
方法的实例

我可以使用哪种类型来暗示此参数

更新: 据我所知,目前还没有这样的类型,我尝试自己制作一个:

类IndexableContainer(通用[int,ReturnType]): def\uu getitem\uuu(self,key:int)->返回类型: ... 但我得到了以下错误:

文件“indexable_container.py”,第22行,在IndexableContainer中
类IndexableContainer(泛型[int,ReturnType]):
文件“../lib/python3.6/typing.py”,第682行,在内部
返回函数(*args,**kwds)
文件“../lib/python3.6/typing.py”,第1112行,在__
“泛型[…]的参数必须都是类型变量”)
TypeError:泛型[…]的参数必须都是类型变量

我该怎么做呢?

看来我们能得到的最接近的结果是:

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
装饰器和联合来对某些输入进行特殊处理,但还有第二种方法,尽管是实验性的,我们称之为

协议基本上允许您使用类型提示以合理的方式表达“duck typing”:基本思想是我们可以调整IndexableContainer,使其成为协议。现在,任何实现带有适当签名的
\uuu getitem\uuuu
方法的对象都被视为有效的IndexableContainer,无论它们是否为该类型的子类

唯一需要注意的是,协议目前是实验性的,并且(afaik)仅由mypy支持。计划是最终将协议添加到通用Python生态系统中——具体建议请参见——但我没有跟踪讨论/不知道其状态如何

在任何情况下,要使用协议,请使用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)
建议。 此类型同时支持
\uuuu getitem\uuuuuuu
\uuuuuu len\uuuuuuu

但是,考虑到它目前已被弃用,我认为使用它会更好

然而,正如作者稍后在评论中提到的,他/她实际上也需要一些
\uuu delitem\uuuu
,在这种情况下可能是最合适的(在评论中@Yuval也提到了这一点)。它支持所有的
\uuuu getitem\uuuuuuuuuuu
\uuuuuuuu setitem\uuuuuuuu
\uuuuuu delitem\uuuuuuuuuu
,以及
插入

最终类型的示例用法(改编自):


如果它是一个iterable,那么你可以使用这个typehint:如果不是,那么你应该创建你自己的typehint类。不幸的是,它必须是可索引的,而不是iterable。我如何创建我自己的typehinting类?你可以在这里找到一个例子:对不起,这个例子不完整,我无法让它工作。你有更好的吗?你只需要提示KeyType和ValueType。例如,如果密钥只能是字符串,则使用
str
。为什么这还不够?你能展示这个类的用法吗?我想它确实适用于上面提到的问题,但我后来意识到我还需要这个类型来支持
\uu delitem\uuuuu
方法,这不是
映射提供的。我不知道需要分别指定
\uu getitem\uuuuuuuuuuu
\uuuuuuu delitem\uuuuuuuuuu
。@Rizhiy您可以使用可变映射记住:
序列
意味着对键有一个顺序,即可以使用
范围(len(var))中的键访问值。
。如果键不是这样的,
映射
更合适。有趣。那会更合适吗?如果您需要修改容器,您确实提到需要
\uu delitem\uuuu
(当我说Yuval提到
MutableSequence
时,我一定误读了他,当他实际说
MutableMapping
)而不是“是”
MutableMapping
更好。
from collections.abc import MutableSequence

def foo(bar: MutableSequence[Any]):
    # bar is a mutable sequence of any objects