我应该使用Python中的枚举迭代列表时创建副本

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (218)

在回答这个问题时,我遇到了一些我从未在Python中思考过的东西(由用户指出)。

基本上,我已经知道(这是一个有趣的线程),我必须在迭代时制作副本,同时在Python中操作列表以避免奇怪的行为。

现在,我的问题是,正在使用enumerate克服这个问题?

test_list = [1,2,3,4]
for index,item in enumerate(test_list):
    if item == 1:
        test_list.pop(index)

这段代码是否会被认为是安全的或我应该使用,

for index,item in enumerate(test_list[:]):
提问于
用户回答回答于

首先,让我们回答您的直接问题:

enumerate在这里没有任何帮助。它就好像它拥有一个底层迭代器的迭代器(至少在CPython中,它正是它所做的),所以任何对列表迭代器不合法或不安全的东西都是不合法或不安全的。使用包含该列表迭代器的枚举对象。

您的原始用例设置test_list[index] = new_value在实践中是安全的 - 但我不确定它是否保证安全。

您新的使用情况下,通话test_list.pop(index)大概-is 不是安全的。

列表迭代器最明显的实现基本上只是对列表的引用和该列表的索引。因此,如果您在当前位置或该位置的左侧插入或删除,您肯定会破坏迭代器。例如,如果你删除lst[i],那就把所有东西都从i + 1一个位置移到最后一个位置,所以当你继续前进时i + 1,你会跳过原来的i + 1值,因为它现在是ith。但是如果你插入或删除当前位置的右边,那不是问题。

由于test_list.pop(index)删除当前位置的左侧或左侧,即使使用此实现也不安全。(当然,如果你仔细编写了你的​​算法,以便在命中之后跳过值永远不会重要,甚至可能就好了。但是更多算法无法处理。)

可以想象,Python实现可以将原始指针存储到用于列表存储的数组中的当前位置。这意味着插入任何地方都可能会破坏迭代器,因为插入可能导致整个列表重新分配到新内存。如果实施有时会在缩小时重新分配列表,那么可以删除任何地方。我不认为Python不允许实现所有这些,所以如果你想要偏执,那么在迭代时永远不会插入或删除更安全。

如果您只是替换现有值,很难想象在任何合理的实现下如何破坏迭代器。但是,据我所知,语言引用和list库引用1实际上并未对列表迭代器的实现做出任何承诺。2

因此,您是否关心“我的实施中的安全”,“每个实施中的每个实施都是安全的”,“在每个可能的(对我而言)实施中安全”,或“通过参考保证安全”取决于您。

我认为大多数人在迭代期间愉快地替换列表项,但避免缩小或增长列表。但是,肯定有生产代码,至少删除了迭代器的权利。

1.我相信这个教程只是说在某个地方永远不会修改任何数据结构,而是迭代它 - 但这就是教程。始终遵循该规则肯定是安全的,但遵循不太严格的规则也可能是安全的。

2.除非key函数或其他任何东西试图以任何方式在a中间访问列表,否则sort结果是未定义的。

用户回答回答于

由于这是我的评论导致这一点,我将添加我的跟进:

枚举可以被认为是一个生成器,所以它只需要一个序列(实际上是任何序列或迭代器),只是“生成”一个递增的索引,以便从传递的序列中的每个项目中产生(因此它不会复制或改变它无论如何只使用“枚举对象”列表。

对于该问题中的代码,您永远不会更改迭代列表的长度,并且一旦运行if语句,元素的值无关紧要。因此不需要副本,当删除元素时将需要它,因为迭代器索引与列表共享并且不考虑已删除的元素。

Python忍者有一个很好的例子,你何时应该使用副本(或移动到列表理解)

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励