迭代器是否在Python中保留自己的数组副本



我有一个简单的程序,它删除列表中的一个元素,同时使用reversed()返回的迭代器。

代码如下:

def removeElement( nums, val ):
for x in reversed(nums):
if x == val:
nums.remove(x)

如果我使用for x in nums:对列表进行迭代,那么当移除一个元素(也是当前元素(时,我们将跳过数组中的元素。然而,在我们使用reversed()返回的迭代器的情况下不会发生这种情况——对remove()的调用似乎对反向列表没有影响。

我还注意到,两个列表中的每个元素都有相同的内存地址,因此reversed()不会分配一个全新的列表。

reversed()返回的迭代器如何保持列表的原始结构,尽管对remove()的调用会影响原始列表?

迭代器是否在Python中保留自己的数组副本?

,或者至少在CPython中没有。我们可以对列表执行一个简单的测试:

>>> l = [1,2]
>>> for x in reversed(l):
...     l[0] = 3
...     print(x)
... 
2
3

因此,在这里我们可以看到,我们的编辑(l[0] = 3(对生成的元素有影响。最后我们看到3。如果reversed(..)首先构造了一个副本,那么它将发出1。对于只有一个元素的列表,这是不起作用的,因为在这种情况下,我们在进入循环主体之前将x设置为1,然后更改列表,对该元素没有影响。

列表定义了一个__reversed__函数,这意味着在reversed(..)调用的情况下,它将使用该函数反向枚举元素。可以在Python中实现这一点:

class list:
# ...
def __reversed__(self):
for i in range(len(self)-1, -1, -1):
if i >= len(self):
yield self[i]
else:
break

在CPython中,它是用一个listreviterobject对象实现的,该对象本质上也有一个索引it_index,在访问下一个项时该索引会递减。我更新了Python版本,使其与CPython实现更加等效。

如果.remove()是一个元素,那么您将移除Python找到的第一个项,因此它可能会"移动"光标下的元素。因此,我们有可能对同一物品进行多次计数。然而,这最终不会产生多大影响。例如:

1 4 4 2 4 4

如果我们删除4,我们将以以下方式执行此操作:

1 4 4 2 4 4   (start cursor at the right)
^
1 4 4 2 4 4   (start cursor at the right)
^
1 4 2 4 4     (remove 4)
^
1 4 2 4 4     (advance cursor)
^
1 2 4 4       (remove 4)
^
1 2 4 4       (advance cursor)
^
1 2 4         (remove 4)
^
1 2 4         (advance cursor)
^
1 2           (remove 4)
^
1 2           (advance cursor)
^
1 2           (advance cursor)
^

因此,如果光标位于我们要删除的元素上,我们将删除该元素的第一个出现。它将始终位于左侧(或光标下方(,因为如果光标位于该位置的左侧,则无法将其放置在该元素上方。

如果它删除了元素,可以说列表的一部分移动到了"光标下"。这意味着,由于光标向左移动,它相对地保持在同一位置。这种情况将一直发生,直到最后一个元素被删除。接下来,光标可以继续向左移动,再也看不到可以删除的元素(因为这些元素已经被删除了(。

一个简单的程序可以产生emperical的证据,证明它是有效的,如下所示:

while True:
l = [randint(0, 9) for _ in range(10)]
x = randint(0, 9)
removeElement(l, x)
assert x not in l

因此,我们在这里生成了一个由10个随机元素组成的列表,并将其删除。最后,我们检查l是否不再包含x元素。这当然是而不是它有效的基本证明,但如果我们运行足够长的时间,它至少为我们提供了一些证据,证明它适用于值在09之间的具有10个元素的列表。

但也就是说,最好是而不是同时修改和迭代集合,无论以何种方式。如果您以后想要更改代码以查找当前和下一个元素,那么这可能会出错。

reversed()的官方文档中写道:

反向(seq(

返回一个反向迭代器。seq必须是具有reversed((方法或支持序列协议的对象(len[(方法和getitem[]方法,整数参数从0开始(。

文档中似乎没有任何关于函数如何工作的进一步保证。

给出了这些文档,我希望A=("a", "b", "c", "d)这样的列表的reversed()的琐碎植入会返回A[3]A[2]A[1],最后返回A[0],我认为给定的实现没有理由存储列表的结构。

相关内容