简体   繁体   English

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

[英]Why does defining __getitem__ on a class make it iterable in python?

Why does defining __getitem__ on a class make it iterable?为什么在类上定义 __getitem__ 使其可迭代?

For instance if I write:例如,如果我写:

class b:
  def __getitem__(self, k):
    return k

cb = b()

for k in cb:
  print k

I get the output:我得到输出:

0
1
2
3
4
5
6
7
8
...

I would really expect to see an error returned from "for k in cb:"我真的希望看到从“for k in cb:”返回的错误

Iteration's support for __getitem__ can be seen as a "legacy feature" which allowed smoother transition when PEP234 introduced iterability as a primary concept. Iteration 对__getitem__的支持可以被看作是一个“遗留特性”,当 PEP234 引入迭代作为主要概念时,它允许更平滑的过渡。 It only applies to classes without __iter__ whose __getitem__ accepts integers 0, 1, &c, and raises IndexError once the index gets too high (if ever), typically "sequence" classes coded before __iter__ appeared (though nothing stops you from coding new classes this way too).它仅适用于没有__iter__的类,其__getitem__接受整数 0、1、&c,并在索引过高(如果有的话)时引发IndexError ,通常是在__iter__出现之前编码的“序列”类(尽管没有什么可以阻止您编写新类)方式太)。

Personally, I would rather not rely on this in new code, though it's not deprecated nor is it going away (works fine in Python 3 too), so this is just a matter of style and taste ("explicit is better than implicit" so I'd rather explicitly support iterability rather than rely on __getitem__ supporting it implicitly for me -- but, not a bigge).就我个人而言,我宁愿不在新代码中依赖它,尽管它没有被弃用也不会消失(在 Python 3 中也可以正常工作),所以这只是风格和品味的问题(“显式优于隐式”所以我宁愿明确地支持可迭代性,而不是依赖__getitem__为我隐式地支持它——但是,不是很大)。

If you take a look at PEP234 defining iterators, it says:如果您查看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__ predates the iterator protocol, and was in the past the only way to make things iterable. __getitem__早于迭代器协议,并且在过去是使事物可迭代的唯一方法。 As such, it's still supported as a method of iterating.因此,它仍然被支持作为一种迭代方法。 Essentially, the protocol for iteration is:本质上,迭代的协议是:

  1. Check for an __iter__ method.检查__iter__方法。 If it exists, use the new iteration protocol.如果存在,则使用新的迭代协议。

  2. Otherwise, try calling __getitem__ with successively larger integer values until it raises IndexError.否则,尝试使用连续较大的整数值调用__getitem__ ,直到它引发 IndexError。

(2) used to be the only way of doing this, but had the disadvantage that it assumed more than was needed to support just iteration. (2) 曾经是这样做的唯一方法,但有一个缺点,即它假设的数量超过了支持迭代所需的数量。 To support iteration, you had to support random access, which was much more expensive for things like files or network streams where going forwards was easy, but going backwards would require storing everything.为了支持迭代,您必须支持随机访问,这对于文件或网络流之类的东西来说要昂贵得多,因为前进很容易,但后退需要存储所有内容。 __iter__ allowed iteration without random access, but since random access usually allows iteration anyway, and because breaking backward compatability would be bad, __getitem__ is still supported. __iter__允许在没有随机访问的情况下进行迭代,但是由于随机访问通常允许迭代,并且因为破坏向后兼容性会很糟糕,所以仍然支持__getitem__

Special methods such as __getitem__ add special behaviors to objects, including iteration. __getitem__等特殊方法向对象添加特殊行为,包括迭代。

http://docs.python.org/reference/datamodel.html#object. http://docs.python.org/reference/datamodel.html#object。 getitem 获取项目

"for loops expect that an IndexError will be raised for illegal indexes to allow proper detection of the end of the sequence." “for 循环期望为非法索引引发 IndexError 以允许正确检测序列的结尾。”

Raise IndexError to signal the end of the sequence.引发 IndexError 以表示序列结束。

Your code is basically equivalent to:您的代码基本上相当于:

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

Where object is what you're iterating over in the for loop.其中 object 是您在 for 循环中迭代的内容。

This is so for historical reasons.这是有历史原因的。 Prior to Python 2.2 __getitem__ was the only way to create a class that could be iterated over with the for loop.在 Python 2.2 之前 __getitem__ 是创建可以用 for 循环迭代的类的唯一方法。 In 2.2 the __iter__ protocol was added but to retain backwards compatibility __getitem__ still works in for loops.在 2.2 中添加了 __iter__ 协议,但为了保持向后兼容性 __getitem__ 仍然适用于 for 循环。

Because cb[0] is the same as cb.__getitem__(0) .因为cb[0]cb.__getitem__(0) See the python documentation on this.请参阅关于此的python 文档

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM