为了账号安全,请及时绑定邮箱和手机立即绑定

如何改变被迭代的容器?

如何改变被迭代的容器?

茅侃侃 2022-06-22 16:21:34
在 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),移除的元素随后不会被迭代,一个元素是否被迭代(或不被迭代)永远不会受到其他元素的添加/删除的影响。我想到的应用程序是事件回调的注册表。触发时,我希望回调能够急切地注册或取消注册同一事件的其他回调。(例如,如果我迭代了容器的临时副本,我需要等待事件再次触发,然后更改开始生效。)
查看完整描述

2 回答

?
青春有我

TA贡献1784条经验 获得超8个赞

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


查看完整回答
反对 回复 2022-06-22
?
holdtom

TA贡献1805条经验 获得超10个赞

您要询问的行为是所涉及的迭代器的实现细节。正如您所注意到的,该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

正如评论所暗示的那样,该循环带有 print out 1、2、4和8.


查看完整回答
反对 回复 2022-06-22
  • 2 回答
  • 0 关注
  • 101 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信