python 反向迭代器与生成器行为

python reverse iterator vs. generator behavior

为什么删除列表中的项目会破坏 reversed 个对象?它不会破坏 gen-exprs,追加或修改列表不会破坏 reversed 对象,而且它显然指向原始对象,那么为什么不能给出截断版本?也许一些例子可以说明:

l = [1, 2, 3, 4]
r = reversed(l)
g = (i for i in l)
l.pop()  # returns 4
l   # returns [1, 2, 3]

for i in g:print(i)  # prints 1 2 3 (on separate lines)
for i in r:print(i)  # prints ...nothing

r = reverse(l)
g = (i for i in l)
l[1] = 4
for i in g:print(i)  # prints 1 4 3 (on separate lines)
for i in r:print(i)  # prints 3 4 1 (on separate lines)

r = reversed(l)
g = (i for i in l)
l.append(5)
l  # returns [1, 4, 3, 5] just to keep you on your toes

for i in g:print(i)  # prints 1 4 3 5 (on separate lines)
for i in r:print(i)  # prints 3 4 1   (on separate lines)

所以 - 如果 genexpr 足够聪明,可以指向对象,并且只响应对象的变化,为什么 reversed 不呢?它显然不会复制,否则它不会在第一种情况下 "fail" ,并且在第二种情况下不会选择 4 。所以它必须指向对象。为什么它不能从索引 -1 开始并向后工作?

当您在列表对象上调用 reversed() 时,会创建一个专用的反向列表迭代器对象;此对象 'knows' 如何以相反的顺序有效地遍历列表 once.

为此,创建对象时,存储列表中的最后一个索引。对于您的列表 l,即 3(第 4 个元素,从 0 开始计数)。然后进行迭代时,会生成该索引处的元素,并且索引会递减,直到引发 IndexError *.

对象的 Python 实现如下所示:

class reversed_list_iterator(object):
    def __init__(self, lst):
        self.index = len(lst) - 1
        self.lst = lst

    def __iter__(self):
        return self

    def __next__(self):
        try:
            result = self.lst[self.index]
        except IndexError:
            self.lst = []  # switch to permanently stopped state
            raise StopIteration
        self.index -= 1
        return result

现在,当您 删除 该元素时,迭代器会退出,因为没有 l[3],引发 IndexError 然后 和迭代结束。

在您的第二个示例中,创建反向迭代器时,最后一个索引是 2。然后您 添加 到列表,但迭代从 l[2] 开始,它仍然存在。

反向列表迭代器不能使用相对索引,因为正如您发现的那样,迭代器对添加到列表中的元素具有相对容忍度。相对索引将重复值。


* actual C implementation 测试边界 0 <= index < len(self.lst) 而不是 catch IndexError,但原理是一样的。