迭代列表时删除列表

2024-12-11 08:47:00
admin
原创
183
摘要:问题描述:以下代码:a = list(range(10)) remove = False for b in a: if remove: a.remove(b) remove = not remove print(a) 使用 Python 3.2 时,输出[0, 2, 3, 5,...

问题描述:

以下代码:

a = list(range(10))
remove = False
for b in a:
    if remove:
        a.remove(b)
    remove = not remove
print(a)

使用 Python 3.2 时,输出[0, 2, 3, 5, 6, 8, 9],而不是。[0, 2, 4, 6, 8]

  1. 为什么它会输出这些特定的值?

  2. 为什么没有给出错误来表明底层迭代器正在被修改?

  3. 相对于早期版本的 Python,这种行为的机制是否有所改变?

请注意,我并不是想解决这种行为,而是想理解它。


解决方案 1:

我犹豫了好一阵子是否要回答这个问题,因为类似的问题在这里已经被问过很多次了。但这个问题足够独特,值得怀疑。(不过,如果其他人投票关闭,我不会反对。)以下是对正在发生的事情的直观解释。

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]       <-  b = 0; remove? no
 ^
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]       <-  b = 1; remove? yes
    ^
[0, 2, 3, 4, 5, 6, 7, 8, 9]          <-  b = 3; remove? no
       ^
[0, 2, 3, 4, 5, 6, 7, 8, 9]          <-  b = 4; remove? yes
          ^
[0, 2, 3, 5, 6, 7, 8, 9]             <-  b = 6; remove? no
             ^
[0, 2, 3, 5, 6, 7, 8, 9]             <-  b = 7; remove? yes
                ^
[0, 2, 3, 5, 6, 8, 9]                <-  b = 9; remove? no
                   ^

由于没有其他人回答,我将尝试回答您的其他问题:

为什么没有给出错误来表明底层迭代器正在被修改?

为了抛出错误而不禁止许多完全有效的循环构造,Python 必须了解很多正在发生的事情,并且它可能必须在运行时获取这些信息。所有这些信息都需要时间来处理。这会让 Python 的速度慢很多,而这正是速度真正重要的地方——循环。

相对于早期版本的 Python,这种行为的机制是否有所改变?

简而言之,不是。或者至少我对此非常怀疑,而且自从我学习 Python(2.4)以来,它的行为肯定就是这样的。坦率地说,我希望任何可变序列的直接实现都以这种方式运行。任何更了解的人,请纠正我。(实际上,快速的文档查找证实了Mikola引用的文本自1.4 版以来就一直存在于教程中!)

解决方案 2:

正如 Mikola 所解释的,您观察到的实际结果是由于从列表中删除一个条目会导致整个列表移动一个位置,从而导致您错过元素。

但在我看来,更有趣的问题是,为什么 Python 在发生这种情况时不选择生成错误消息。如果您尝试修改字典,它确实会生成这样的错误消息。我认为这有两个原因。

  1. 字典内部很复杂,而列表则不然。列表基本上就是数组。字典必须在迭代过程中检测其修改时间,以避免在字典内部结构发生变化时崩溃。列表可以不进行该检查,因为它只是确保其当前索引仍在范围内。

  2. 从历史上看(我现在不确定),python 列表是使用 [] 运算符进行迭代的。Python 会评估 list[0]、list[1]、list[2],直到它得到 IndexError。在这种情况下,python 在开始之前不会跟踪列表的大小,因此它没有方法检测列表的大小是否已更改。

解决方案 3:

当然,在迭代数组时修改它并不安全。规范说这是一个坏主意,并且行为未定义:

http://docs.python.org/tutorial/controlflow.html#for-statements

那么,下一个问题是,这里到底发生了什么?如果让我猜的话,我会说它正在做这样的事情:

for(int i=0; i<len(array); ++i)
{
   do_loop_body(i);
}

如果您认为这确实是正在发生的事情,那么它完全解释了观察到的行为。当您删除当前指针处或之前的元素时,您会将整个列表向左移动 1。第一次,您像往常一样删除 1,但现在列表向后移动。下一次迭代不是达到 2,而是达到 3。然后您删除 4,列表向后移动。下一次迭代 7,依此类推。

解决方案 4:

如果在 for 循环中添加 reversed(),则可以向后遍历数组,同时删除项目并获得预期的输出。数组中元素的位置取决于前面的元素,而不是后面的元素:

因此代码:

a = list(range(10))
remove = True
for b in reversed(a):
    if remove:
        a.remove(b)
    remove = not remove
print(a)

产生预期的结果:[0, 2, 4, 6, 8]

解决方案 5:

在第一次迭代中,您无需删除,一切都很顺利。

第二次迭代时,您位于序列中的位置 [1],然后删除“1”。然后迭代器将您带到序列中的位置 [2],现在为“3”,因此跳过了“2”(因为删除后“2”现在位于位置 [1])。当然“3”不会被删除,因此您继续到序列中的位置 [3],现在为“4”。删除该位置后,您将转到位置 [5],现在为“6”,依此类推。

事实上,删除东西意味着每次执行删除操作时都会跳过一个位置。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   3975  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   2742  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Freshdesk、ClickUp、nTask、Hubstaff、Plutio、Productive、Targa、Bonsai、Wrike。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在项目管理过程中面临着诸多痛点,如任务分配不...
项目管理系统   80  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Monday、TeamGantt、Filestage、Chanty、Visor、Smartsheet、Productive、Quire、Planview。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多项目经理和团队在管理复杂项目时,常...
开源项目管理工具   88  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Smartsheet、GanttPRO、Backlog、Visor、ResourceGuru、Productive、Xebrio、Hive、Quire。在当今快节奏的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在选择项目管理工具时常常面临困惑:...
项目管理系统   77  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用