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

为什么在类上定义 _ _ 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:”返回一个错误

32101 次浏览

因为 cb[0]cb.__getitem__(0)是一样的。

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

Http://docs.python.org/reference/datamodel.html#object.abc0

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

提高 IndexError 以发出序列结束的信号。

你的代码基本上等同于:

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

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

如果你看一下定义迭代器的 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__的支持可以看作是一个“遗留特性”,当 PEP234引入可迭代性作为一个主要概念时,它允许更平滑的转换。它只适用于没有 __iter__的类,这些类的 __getitem__接受整数0,1,& c,并在指数过高时(如果有的话)提高 IndexError,通常在 __iter__出现之前编码的“序列”类(尽管没有什么能阻止你用这种方式编码新类)。

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

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

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

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

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

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