在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
被跳过)。似乎列表迭代是在索引之上而不是元素之上,因此在迭代过程中只能安全地删除后续列表项(而不是先前列表项)。
标准库中是否有任何容器允许在迭代过程中添加和删除元素,其行为如下:
list.append
一样)我想到的应用程序是事件回调的注册表。触发后,我希望这些回调能够急切地注册或注销同一事件的其他回调。 (例如,如果我遍历了容器的临时副本,则需要等待事件再次触发,然后更改才能生效。)
答案 0 :(得分:3)
您可以通过使用list
方法的适当实现子类化remove
的行为,以自定义from weakref import WeakSet
class IterList:
def __init__(self, lst):
self.list = lst
self.index = 0
def __next__(self):
if self.index == len(self.list):
raise StopIteration
value = self.list[self.index]
self.index += 1
return value
class List(list):
iterators = WeakSet()
def __iter__(self):
iterator = IterList(self)
self.iterators.add(iterator)
return iterator
def remove(self, item):
index = super().index(item)
for iterator in self.iterators:
if index < iterator.index:
iterator.index -= 1
del self[index]
的行为,该方法将在删除的索引小于当前迭代器索引时减少迭代器指向的索引:
container = List((1, 2, 3, 4))
for i in container:
if i == 2:
container.remove(1)
for j in container:
print(i, j)
这样:
1 1
1 2
1 3
1 4
2 2
2 3
2 4
3 2
3 3
3 4
4 2
4 3
4 4
输出:
struct Info {
let author, title: String
let tags: [Tags]
let price: [Double]
enum Tags: String, Codable {
case nonfiction, biography, fiction
}
}
答案 1 :(得分:1)
您要询问的行为是所涉及的迭代器的实现细节。正如您所注意到的,list_iterator
类型使用内部索引,因此删除已访问的元素会导致问题,因为它会更改列表中所有更高值的索引。
我建议您实际上没有从列表中删除任何值。而是将它们添加到另一个容器中,也许是set
(如果它们是可哈希的)。这假定值是唯一的。但是,如果不是这样,您可能会遇到用任何方法将它们从列表中删除的问题。
container = [1, 2, 3, 4]
removed = set()
for i in container:
if i not in removed: # skip values that have been "removed"
print(i)
if i == 2:
removed.add(1) # since we've already visited 1, this has no real effect
removed.add(3) # this does work though, we won't print the 3
container.append(8) # additions of new elements work as normal
正如评论所建议的那样,该循环将打印出1
,2
,4
和8
。