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

问题描述 投票:0回答:6

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

python iterator overloading
6个回答
79
投票

Iteration 对

__getitem__
的支持可以看作是一个“遗留特性”,当 PEP234 将可迭代性作为主要概念引入时,它允许更平滑的过渡。它仅适用于没有
__iter__
的类,其
__getitem__
接受整数 0、1、&c,并在索引变得太高时引发
IndexError
(如果有的话),通常是在
__iter__
出现之前编码的“序列”类(尽管也没有什么能阻止你用这种方式编写新类)。

就个人而言,我宁愿不在新代码中依赖它,尽管它没有被弃用也不会消失(在 Python 3 中也能正常工作),所以这只是风格和品味的问题(“显式优于隐式”所以我宁愿明确支持可迭代性,也不愿依赖

__getitem__
为我隐式支持它——但不是很大)。


55
投票

如果你看一下 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().

42
投票

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

  1. 检查

    __iter__
    方法。如果存在,则使用新的迭代协议。

  2. 否则,尝试调用

    __getitem__
    连续更大的整数值,直到它引发 IndexError。

(2) 曾经是执行此操作的唯一方法,但缺点是它假设的数量超出了仅支持迭代所需的数量。为支持迭代,您必须支持随机访问,这对于文件或网络流之类的东西来说要昂贵得多,在这些地方前进很容易,但后退则需要存储所有内容。

__iter__
允许在没有随机访问的情况下进行迭代,但是由于随机访问通常无论如何都允许迭代,并且因为打破向后兼容性会很糟糕,因此仍然支持
__getitem__


8
投票

__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 循环中迭代的对象。


5
投票

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


1
投票

因为

cb[0]
cb.__getitem__(0)
相同。请参阅 python 文档

© www.soinside.com 2019 - 2024. All rights reserved.