如何改变被迭代的容器?

在 python 中,哪些容器在迭代期间正确支持突变?


例如:


container = [1, 2, 3, 4]

for i in container:

    print(i)

    if i == 2:

        container.append(8)

输出1 2 3 4 8(可以在迭代期间附加列表)。

但是,如果我.append(8).remove(1)输出代替1 2 4(即元素3被跳过)。似乎列表迭代超出了索引而不是元素,因此只有后续列表项(而不是先前的列表项)可以在迭代期间安全地删除。

标准库中是否有任何容器允许在迭代期间添加和删除元素,其行为是:

  1. 新元素确实会被迭代(对于list.append),

  2. 移除的元素随后不会被迭代,

  3. 一个元素是否被迭代(或不被迭代)永远不会受到其他元素的添加/删除的影响。

我想到的应用程序是事件回调的注册表。触发时,我希望回调能够急切地注册或取消注册同一事件的其他回调。(例如,如果我迭代了容器的临时副本,我需要等待事件再次触发,然后更改开始生效。)


茅侃侃
浏览 102回答 2
2回答

青春有我

list您可以通过使用适当的方法实现对其进行子类化来自定义行为,remove当被删除的索引小于当前迭代器索引时,该方法会减少迭代器指向的索引:from weakref import WeakSetclass IterList:&nbsp; &nbsp; def __init__(self, lst):&nbsp; &nbsp; &nbsp; &nbsp; self.list = lst&nbsp; &nbsp; &nbsp; &nbsp; self.index = 0&nbsp; &nbsp; def __next__(self):&nbsp; &nbsp; &nbsp; &nbsp; if self.index == len(self.list):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; raise StopIteration&nbsp; &nbsp; &nbsp; &nbsp; value = self.list[self.index]&nbsp; &nbsp; &nbsp; &nbsp; self.index += 1&nbsp; &nbsp; &nbsp; &nbsp; return valueclass List(list):&nbsp; &nbsp; iterators = WeakSet()&nbsp; &nbsp; def __iter__(self):&nbsp; &nbsp; &nbsp; &nbsp; iterator = IterList(self)&nbsp; &nbsp; &nbsp; &nbsp; self.iterators.add(iterator)&nbsp; &nbsp; &nbsp; &nbsp; return iterator&nbsp; &nbsp; def remove(self, item):&nbsp; &nbsp; &nbsp; &nbsp; index = super().index(item)&nbsp; &nbsp; &nbsp; &nbsp; for iterator in self.iterators:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if index < iterator.index:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iterator.index -= 1&nbsp; &nbsp; &nbsp; &nbsp; del self[index]以便:container = List((1, 2, 3, 4))for i in container:&nbsp; &nbsp; if i == 2:&nbsp; &nbsp; &nbsp; &nbsp; container.remove(1)&nbsp; &nbsp; for j in container:&nbsp; &nbsp; &nbsp; &nbsp; print(i, j)输出:1 11 21 31 42 22 32 43 23 33 44 24 34 4

holdtom

您要询问的行为是所涉及的迭代器的实现细节。正如您所注意到的,该list_iterator类型使用内部索引,因此删除已访问的元素会导致问题,因为它会更改列表中所有后续值的索引。我的建议是您实际上并没有从列表中删除任何值。相反,将它们添加到另一个容器中,也许是一个set(如果它们是可散列的)。这假设值是唯一的。但如果不是,您可能会在使用任何方法从列表中删除它们时遇到问题。container = [1, 2, 3, 4]removed = set()for i in container:&nbsp; &nbsp; if i not in removed:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# skip values that have been "removed"&nbsp; &nbsp; &nbsp; &nbsp; print(i)&nbsp; &nbsp; &nbsp; &nbsp; if i == 2:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; removed.add(1)&nbsp; &nbsp; &nbsp; &nbsp;# since we've already visited 1, this has no real effect&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; removed.add(3)&nbsp; &nbsp; &nbsp; &nbsp;# this does work though, we won't print the 3&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; container.append(8)&nbsp; # additions of new elements work as normal正如评论所暗示的那样,该循环带有 print out 1、2、4和8.
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Python