为什么在类上定义 __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:”返回的错误
Iteration 对
__getitem__
的支持可以看作是一个“遗留特性”,当 PEP234 将可迭代性作为主要概念引入时,它允许更平滑的过渡。它仅适用于没有 __iter__
的类,其 __getitem__
接受整数 0、1、&c,并在索引变得太高时引发 IndexError
(如果有的话),通常是在 __iter__
出现之前编码的“序列”类(尽管也没有什么能阻止你用这种方式编写新类)。
就个人而言,我宁愿不在新代码中依赖它,尽管它没有被弃用也不会消失(在 Python 3 中也能正常工作),所以这只是风格和品味的问题(“显式优于隐式”所以我宁愿明确支持可迭代性,也不愿依赖
__getitem__
为我隐式支持它——但不是很大)。
如果你看一下 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().
__getitem__
早于迭代器协议,并且在过去是 only 使事情可迭代的方式。因此,它仍然作为一种迭代方法受到支持。本质上,迭代协议是:
检查
__iter__
方法。如果存在,则使用新的迭代协议。否则,尝试调用
__getitem__
连续更大的整数值,直到它引发 IndexError。(2) 曾经是执行此操作的唯一方法,但缺点是它假设的数量超出了仅支持迭代所需的数量。为支持迭代,您必须支持随机访问,这对于文件或网络流之类的东西来说要昂贵得多,在这些地方前进很容易,但后退则需要存储所有内容。
__iter__
允许在没有随机访问的情况下进行迭代,但是由于随机访问通常无论如何都允许迭代,并且因为打破向后兼容性会很糟糕,因此仍然支持__getitem__
。
__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
object 是你在 for 循环中迭代的对象。
这是出于历史原因。在 Python 2.2 之前,__getitem__ 是创建可以使用 for 循环迭代的类的唯一方法。在 2.2 中添加了 __iter__ 协议,但为了保持向后兼容性,__getitem__ 仍然适用于循环。