为什么在类上定义__getitem__使它在python中可迭代?

时间:2009-05-29 15:22:53

标签: python iterator overloading

为什么在类上定义__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:”

返回错误

6 个答案:

答案 0 :(得分:60)

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

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

答案 1 :(得分:46)

如果你看一下定义迭代器的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().

答案 2 :(得分:25)

__getitem__早于迭代器协议,并且过去只使用 方式使事物可迭代。因此,它仍然作为迭代方法得到支持。从本质上讲,迭代协议是:

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

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

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

答案 3 :(得分:6)

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

http://docs.python.org/reference/datamodel.html#object.getitem

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

引发IndexError以表示序列的结束。

您的代码基本上等同于:

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

对象就是你在for循环中迭代的对象。

答案 4 :(得分:5)

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

答案 5 :(得分:2)

因为cb[0]cb.__getitem__(0)相同。请参阅此处的python documentation