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

时间:2015-08-13 23:57:50

标签: python python-3.x iterator generator reverse

为什么删除项目列表中断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?它显然不会复制,否则它不会在第一种情况下“失败”,并且它不会在第二种情况下获得4。所以它必须指向对象。为什么它不能从索引-1开始并向后工作?

1 个答案:

答案 0 :(得分:6)

当您在列表对象上调用reversed()时,会创建一个专用的反向列表迭代器对象;这个对象'知道'如何以相反的顺序有效地迭代列表一次

为此,在创建对象时,将存储列表中的最后一个索引。对于您的列表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)而不是捕获IndexError,但原理是相同的。