【问题标题】:python reverse iterator vs. generator behaviorpython反向迭代器与生成器行为
【发布时间】:2015-11-07 03:13:19
【问题描述】:

为什么删除列表中的项目会破坏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)

所以 - 如果geneexpr 足够聪明,可以指向对象,并且只要响应对象已经改变,为什么reversed 不呢?它显然不会复制,否则它不会在第一种情况下“失败”,也不会在第二种情况下拿起4。所以它必须指向对象。为什么不能从索引-1 开始并向后工作?

【问题讨论】:

  • 我很确定当您在迭代器运行时修改可迭代的内容时,Python 迭代器(包括 reversed 迭代器)的行为是未指定的,因此您不应该对不同的结果感到惊讶.一些迭代器可能会因错误而停止(字典迭代器会),而其他迭代器可能会提前退出(例如您的问题所涉及的列表反向迭代器),或者跳过或重复元素。

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


【解决方案1】:

当您在列表对象上调用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,但原理是一样的。

【讨论】:

  • 我想我的问题是:如果不是硬编码len(l),而是用硬编码的-1 代替,效率会降低吗?
  • @dwanderson:行为会改变,因为现在附加到列表会改变迭代器将移动到下一个的位置。添加 5 个元素,然后将迭代器设置回这么多。
  • 嗯,好的,绝对正确。我可以看到这是预期的行为。但我想如果它有这样做的历史,那可能会破坏事情。
  • 哦,关于 C 实现的有趣旁注,感谢您的挖掘 :)。不过看看它,line 2918 看起来好像在调用next 期间需要seq 的大小,这...哦,好吧:它以it_index 开始,是原始seq 长度,然后我缩短列表,使if 失败,它认为它是空的。这是有道理的,至少为什么我会看到我的行为。
  • @dwanderson:它检测到索引超出范围(如果在 Python 代码中使用会引发 IndexError)。
猜你喜欢
  • 2010-10-27
  • 2018-05-24
  • 1970-01-01
  • 1970-01-01
  • 2017-04-06
  • 2018-11-01
  • 2012-11-11
  • 1970-01-01
  • 2018-05-16
相关资源
最近更新 更多