Python:如何在迭代列表时从列表中删除元素而不跳过未来的迭代

Posted

技术标签:

【中文标题】Python:如何在迭代列表时从列表中删除元素而不跳过未来的迭代【英文标题】:Python: How to remove elements from list while iterating through it without skipping future iterations 【发布时间】:2021-07-16 16:27:04 【问题描述】:

在 python 中,我注意到如果我使用for x in y 遍历列表,并在循环中删除y 的元素,最后一个元素将被“跳过” - 我假设这是因为len(y) 已更改。

我正在尝试获取具有特定扩展名的所有文件,但满足某些条件的文件除外。

这是原始代码:

def test_print_numTXTs(fileList):
    counter = 0
    for file in fileList:
        if file.name[-4:] == ".txt":
            counter +=1
            if file.name == "a.txt":
                fileList.remove(file)   #problem caused here
    print(counter)
    print(len(fileList))

counter 的输出比 .txt 文件的总数少一。通过调试器,我可以看到它正在跳过循环的最后一次迭代(我假设因为 len(fileList) 现在是 -=1 w.r.t. 它的初始 len()

下面的代码“有效”,但感觉像是一个 hack - 我正在将我想从列表中删除的文件添加到第二个列表中,然后在事后对其进行迭代。我已经注释掉了我原来的行,这导致了迭代的“跳过”。

def print_numTXTs(fileList):
    filesToRemoveFromList = []
    counter = 0
    for file in fileList:
        if file.name[-4:] == ".txt":
            counter +=1
            if file.name == "a.txt":
                #fileList.remove(file) #problem caused here
                filesToRemoveFromList.append(file)
    print(counter)
    for file in filesToRemoveFromList:
        fileList.remove(file)
    print(len(fileList))

此代码输出所有 .txt 文件的计数,并且列表的长度比该长度小一(因为元素 a.txt 已被删除) - 这是所需的行为。

这个问题有更优雅的解决方案吗?

【问题讨论】:

fileList = [f for f in fileList if f != 'a.txt']…!? 【参考方案1】:

你是对的。你需要一个额外的清单。但是有一个更简单的解决方案。

def print_numTXTs(fileList):

    counter = 0
    for file in list(fileList):
        if file.name[-4:] == ".txt":
            counter +=1
            if file.name == "a.txt":
                fileList.remove(file)
   

秘密是“list(fileList)”。您创建了一个额外的列表并对其进行迭代。

列表压缩同样强大。在您的示例中,它应该像这样工作。我现在还没试过……只是很快写到这里。

fileList = [ file for file in fileList if file.name != "a.txt" ]

【讨论】:

【参考方案2】:

我建议忽略最后一个循环:

def test_print_numTXTs(fileList):
    counter = 0
    res = []
    for file in fileList:
        if file.name[-4:] == ".txt":
            counter +=1
            if file.name != "a.txt":
                res.append(file)   #problem caused here
    print(res)

此解决方案有效。我会考虑它们是否是一种更 Pythonic 的方式。

【讨论】:

【参考方案3】:

不用手动过滤以.txt 结尾的文件,您可以glob 过滤匹配此模式的文件

说文件夹foo包含文件:

a.txt  
b.txt  
c.txt 

你想计算*.txt文件的数量,除了a.txt

>>> from pathlib import Path
>>> file_list = Path('foo').glob('*.txt')
>>> sum(1 for f in file_list if f.name.endswith('.txt') and f.name != 'a.txt')
2

【讨论】:

以上是关于Python:如何在迭代列表时从列表中删除元素而不跳过未来的迭代的主要内容,如果未能解决你的问题,请参考以下文章

射击时从列表中删除外星人 - 找出循环; '表达式:不能增加结束列表迭代器'

如何迭代Python列表并删除类似的元素?

Python在迭代器中删除列表元素

如何在迭代字典时从字典中删除项目?

射击时从列表中删除外星人 - 找出循环; '表达式:无法增加结束列表迭代器'

迭代时如何从通用列表中删除元素?