例如,如果我写下以下代码:
class B:
def __getitem__(self, k):
return k
cb = B()
for k in cb:
print k
我得到了输出:
0
1
2
3
4
5
...
我真的希望能从for k in cb:
中返回一个错误。
class B:
def __getitem__(self, k):
return k
cb = B()
for k in cb:
print k
0
1
2
3
4
5
...
我真的希望能从for k in cb:
中返回一个错误。
迭代器对__getitem__
的支持可以被视为一个“遗留特性”,它允许在PEP234引入可迭代性作为主要概念时更加平滑地过渡。它仅适用于没有__iter__
的类,其__getitem__
接受整数0、1等,并在索引过高(如果有)时引发IndexError
,通常是在__iter__
出现之前编写的“序列”类(尽管你也可以用这种方式编写新类)。
个人而言,在新代码中我宁愿不依赖于此,虽然它并未被弃用,也不会消失(在Python 3中也可以正常工作),因此这只是一种风格和品味的问题(“显式优于隐式”,所以我宁愿明确地支持可迭代性,而不是依赖于__getitem__
隐式地支持它——但这不是什么大问题)。
如果您查看定义迭代器的PEP234,它会说:
如果一个对象实现了
__iter__()
或__getitem__()
,则可以使用for
进行迭代。如果一个对象实现了
next()
,则可以将其作为迭代器使用。
__getitem__
比迭代器协议更早出现,曾经是使对象可迭代的唯一方法。因此,它仍然被支持作为迭代的一种方式。迭代的协议如下:
检查是否存在__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
在for循环中,object是您要迭代的内容。
这是由于历史原因造成的。在 Python 2.2 之前,__getitem__ 是创建可用 for 循环遍历的类的唯一方法。在 2.2 中,__iter__ 协议被添加,但为了保持向后兼容性,__getitem__ 在 for 循环中仍然起作用。
__getitem__
来创建可迭代对象时,是否考虑了使用[]
属性访问的情况?从阅读答案来看,如果您想要[]
属性访问和可迭代性,则不能仅使用__getitem__
。因此,您应该使用__getitem__
进行[]
访问,并使用__iter__
进行迭代器,对吗? - malana__getitem__
的观点是,任何整数类型都会有一个上限,因此索引访问可能会遇到这个问题。而__next__
可以一直愉快地进行下去。 (至少,那是我的理解。) - Jon Coombs