73

为什么在类上定义 __getitem__ 使其可迭代?

例如,如果我写:

class b:
  def __getitem__(self, k):
    return k

cb = b()

for k in cb:
  print k

我得到输出:

0
1
2
3
4
5
6
7
8
...

我真的希望看到“for k in cb:”返回错误

4

6 回答 6

73

__getitem__当 PEP234 将可迭代性作为主要概念引入时,迭代对 的支持可以被视为“遗留功能”,它允许更平滑的过渡。它仅适用于不接受整数 0、1 和 c__iter__的类,并且一旦索引变得太高(如果有的话)就会引发,通常是在出现之前编码的“序列”类(尽管没有什么能阻止你以这种方式编码新类)。__getitem__IndexError__iter__

就个人而言,我宁愿不要在新代码中依赖它,尽管它没有被弃用也不会消失(在 Python 3 中也可以正常工作),所以这只是风格和品味的问题(“显式优于隐式”所以我宁愿明确地支持可迭代性,而不是依赖于__getitem__隐含地支持它——但是,不是很大)。

于 2009-05-29T15:37:35.130 回答
54

如果您看一下定义迭代器的PEP234,它会说:

1. An object can be iterated over with "for" if it implements
   __iter__() or __getitem__().

2. An object can function as an iterator if it implements next().
于 2009-05-29T15:29:22.177 回答
39

__getitem__早于迭代器协议,并且在过去是使事物可迭代的唯一方法。因此,它仍然被支持作为一种迭代方法。本质上,迭代的协议是:

  1. 检查__iter__方法。如果存在,请使用新的迭代协议。

  2. 否则,尝试__getitem__使用连续更大的整数值调用,直到引发 IndexError。

(2) 曾经是这样做的唯一方法,但缺点是它假设了支持迭代所需的更多内容。为了支持迭代,您必须支持随机访问,这对于文件或网络流之类的东西来说要昂贵得多,因为它们向前很容易,但向后移动则需要存储所有内容。 __iter__允许没有随机访问的迭代,但由于随机访问通常允许迭代,并且因为破坏向后兼容性会很糟糕,__getitem__所以仍然支持。

于 2009-05-29T15:38:10.153 回答
8

特殊方法,例如__getitem__向对象添加特殊行为,包括迭代。

http://docs.python.org/reference/datamodel.html#object。获取项目

“for 循环期望会为非法索引引发 IndexError,以允许正确检测序列的结尾。”

引发 IndexError 表示序列结束。

您的代码基本上相当于:

i = 0
while True:
    try:
        yield object[i]
        i += 1
    except IndexError:
        break

其中 object 是您在 for 循环中迭代的内容。

于 2009-05-29T15:28:06.157 回答
5

之所以如此,是因为历史原因。在 Python 2.2 之前,__getitem__ 是创建可以用 for 循环迭代的类的唯一方法。在 2.2 中添加了 __iter__ 协议,但为了保持向后兼容性 __getitem__ 仍然适用于 for 循环。

于 2009-05-29T15:38:23.337 回答
2

因为cb[0]是一样的cb.__getitem__(0)。请参阅有关此的python 文档

于 2009-05-29T15:26:14.180 回答