在 Python 中迭代列表并删除项目时出现奇怪的结果

2024-11-15 08:37:00
admin
原创
16
摘要:问题描述:我有这段代码:numbers = list(range(1, 50)) for i in numbers: if i < 20: numbers.remove(i) print(numbers) 但是,我得到的结果是: [2, 4, 6, 8, 10, 12, 1...

问题描述:

我有这段代码:

numbers = list(range(1, 50))

for i in numbers:
    if i < 20:
        numbers.remove(i)

print(numbers)

但是,我得到的结果是:

[2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49]

当然,我希望 20 以下的数字不会出现在结果中。看来我的删除操作出了问题。


解决方案 1:

您在迭代列表时会修改列表。这意味着第一次循环时,i == 1,因此1会从列表中删除。然后循环for转到列表中的第二项,它不是2,而是3!然后从列表中删除它,然后循环for转到列表中的第三项,现在为 5。依此类推。也许这样更容易形象化,用 ^ 指向 的值i

[1, 2, 3, 4, 5, 6...]
 ^

这是列表的初始状态;然后1被删除并且循环转到列表中的第二项:

[2, 3, 4, 5, 6...]
    ^
[2, 4, 5, 6...]
       ^

等等。

在迭代列表时,没有好的方法来改变列表的长度。你能做的最好的事情是这样的:

numbers = [n for n in numbers if n >= 20]

或者这样进行就地修改(括号中的内容是生成器表达式,在切片赋值之前隐式转换为元组):

numbers[:] = (n for n in numbers if n >= 20)

如果您想n在删除之前执行某项操作,您可以尝试以下技巧:

for i, n in enumerate(numbers):
    if n < 20:
        print("do something")
        numbers[i] = None
numbers = [n for n in numbers if n is not None]

解决方案 2:

从列表末尾开始向后追溯:

li = list(range(1, 15))
print(li)

for i in range(len(li) - 1, -1, -1):
    if li[i] < 6:
        del li[i]
        
print(li)

结果:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] 
[6, 7, 8, 9, 10, 11, 12, 13, 14]

解决方案 3:

@senderle 的回答是正确的!

话虽如此,为了进一步说明你的问题,如果你仔细想想,你总是想删除索引 0 二十次:

[1,2,3,4,5............50]
 ^
[2,3,4,5............50]
 ^
[3,4,5............50]
 ^

因此你实际上可以采用如下方式:

aList = list(range(50))
i = 0
while i < 20:
    aList.pop(0)
    i += 1

print(aList) #[21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49]

我希望它有帮助。


据我所知,下面这些都不是坏做法。

编辑(更多):

lis = range(50)
lis = lis[20:]

也会做这项工作。

EDIT2(我很无聊):

functional = filter(lambda x: x> 20, range(50))

解决方案 4:

所以我找到了一个解决方案,但它真的很笨拙......

首先,创建一个索引数组,其中列出要删除的所有索引,如下所示

numbers = range(1, 50)
index_arr = []

for i in range(len(numbers):
    if numbers[i] < 20:
        index_arr.append(i)

之后,您想要从数字列表中删除所有条目,索引保存在 index_arr 中。您将遇到的问题与之前相同。因此,在从数字 arr 中删除一个数字后,您必须从 index_arr 中的每个索引中减去 1,如下所示:

numbers = range(1, 50)
index_arr = []

for i in range(len(numbers):
    if numbers[i] < 20:
        index_arr.append(i)

for del_index in index_list:
    numbers.pop(del_index)

    #the nasty part
    for i in range(len(index_list)):
        index_list[i] -= 1

它可以工作,但我猜这不是预期的方法

解决方案 5:

作为@Senderle 答案的附加信息,仅供记录,我认为当 python 看到for序列类型”时,可视化场景背后的逻辑会很有帮助。

假设我们有:

lst = [1, 2, 3, 4, 5]

for i in lst:
    print(i ** 2)

事实上将会是:

index = 0
while True:
    try:
        i = lst.__getitem__(index)
    except IndexError:
        break
    print(i ** 2)
    index += 1

这就是它,for当我们在 Sequence 类型或 Iterables 上使用它时,有一个 try-catch 机制(尽管它有点不同 - 调用next()StopIteration异常)。

*我想说的是,python 会跟踪这里一个名为的独立变量index,所以无论列表发生什么(删除或添加),python 都会增加该变量并__getitem__()使用“这个变量”调用方法并请求项目。

解决方案 6:

在@eyquem 的答案基础上进行简化......

问题在于,当你进行迭代时,元素会被从你身下抽出,在你前进到下一个数字时会跳过一些数字

如果从末尾开始并向后移动,则删除当前项目并不重要,因为当它移动到“下一个”项目(实际上是前一个项目)时,删除不会影响列表的前半部分。

只需reversed()在迭代器中添加即可解决问题。注释是一种很好的形式,可以防止未来的开发人员“整理”您的代码并神秘地破坏它。

for i in reversed(numbers): # `reversed` so removing doesn't foobar iteration
  if i < 20:
    numbers.remove(i)

解决方案 7:

对 for 迭代进行浅拷贝,将确保用于迭代的列表不会被修改。可以使用、 或numbers进行浅拷贝,并将确保要删除的元素的标识相同,并减少临时列表的大小。代码:list()`copy.copy()`

...
for i in list(numbers):
    if i < 20:
        numbers.remove(i)
...

解决方案 8:

您还可以使用 continue忽略小于 20 的值

mylist = []

for i in range(51):
    if i<20:
        continue
    else:
        mylist.append(i)
print(mylist)

解决方案 9:

从Python 3.3开始,你可以使用 listcopy()方法作为迭代器:

numbers = list(range(1, 50))

for i in numbers.copy():
    if i < 20:
        numbers.remove(i)
print(numbers)

[20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49]

解决方案 10:

您可以使用list()创建numbers不同的副本,numbers如下所示:

numbers = list(range(1, 50))
       # ↓ ↓ Here ↓ ↓
for i in list(numbers):
    if i < 20:
        numbers.remove(i)

print(numbers) # [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 
               #  31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 
               #  f42, 43, 44, 45, 46, 47, 48, 49]

解决方案 11:

好吧,我需要这样一种方式。

答案是使用while语句。

它确实对我有帮助。所以我想把它作为答案发布出来。

numbers = list(range(1, 50))
while numbers:
    current_number = numbers[0]
    if current_number < 20:
        numbers.remove(current_number)
    else:
        break
numbers
>>> [20, 21, 22, 23, 24, 25, ..., 49]

我还想使用whilefor使用添加我的特殊情况[:],但它可能超出范围,所以我会保留它。

解决方案 12:

有点晚了,但我觉得还是要补充一下我的答案。我发现了以下技巧,非常简单

for x in list[::-1]:
    if validate(x):
        list.pop(x)

这样,您从末尾开始迭代。当您删除第 n 个元素时,元素 n+1、n+2... 的索引会减少一,但因为您要向后移动,所以这不会影响后面(或在本例中为前面)的元素。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

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

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

内置subversion和git源码管理

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

免费试用