为什么在类上定义__getitem__会使其在python中可迭代?
- 2025-02-25 09:07:00
- admin 原创
- 18
问题描述:
为什么__getitem__
在类上定义会使其可迭代?
例如如果我写:
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:
。
解决方案 1:
迭代的支持__getitem__
可以看作是一项“遗留功能”,当PEP234将可迭代性作为主要概念引入时,它允许更平稳的过渡。它仅适用于没有接受整数 0、1 等的类,__iter__
并且__getitem__
一旦IndexError
索引变得太高(如果有的话)就会增加,通常是之前编码的“序列”类__iter__
(尽管没有什么可以阻止您以这种方式编码新类)。
就我个人而言,我宁愿不在新代码中依赖它,尽管它没有被弃用也不会消失(在 Python 3 中也能正常工作),所以这只是一个风格和品味的问题(“显式优于隐式”所以我宁愿明确支持可迭代性,而不是依赖于__getitem__
隐式支持它——但这不是什么大问题)。
解决方案 2:
如果你看一下PEP234定义迭代器,它说:
for
如果对象实现了__iter__()
或 ,就可以用 进行迭代__getitem__()
。如果对象实现了,它就可以充当迭代器的功能
next()
。
解决方案 3:
__getitem__
早于迭代器协议,并且在过去是使事物可迭代的唯一方法。因此,它仍然被支持作为一种迭代方法。本质上,迭代的协议是:
检查
__iter__
方法。如果存在,则使用新的迭代协议。否则,尝试
__getitem__
使用连续更大的整数值进行调用,直到引发 IndexError。
(2) 曾经是实现此目的的唯一方法,但其缺点是它假设了比仅支持迭代所需的更多内容。要支持迭代,您必须支持随机访问,这对于文件或网络流等内容来说要昂贵得多,因为向前访问很容易,但向后访问则需要存储所有内容。 __iter__
允许迭代而不进行随机访问,但由于随机访问通常允许迭代,并且破坏向后兼容性会很糟糕,__getitem__
因此仍然受支持。
解决方案 4:
特殊方法,例如__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 循环中进行迭代的对象。
解决方案 5:
这是出于历史原因。在 Python 2.2 之前,__getitem__ 是创建可以使用 for 循环进行迭代的类的唯一方法。在 2.2 中,添加了 iter 协议,但为了保持向后兼容性,__getitem__ 仍可在 for 循环中使用。
解决方案 6:
因为cb[0]
与 相同cb.__getitem__(0)
。请参阅Python 文档。