遍历列表并删除 Python 中的条目

Looping over list and removing entries in Python

我想遍历 Python 中的列表并删除特定项目。 我不想制作一个新的接受项目列表,因为在我的完整示例中,我想对我的列表进行一系列改进。这是一个简单的示例,其中我尝试删除列表中所有小于 3 的数字。

example = [1.,2.,3.,4.,5.,6.]
for e in example:
  if e < 3.:
    print "Removing:", e
    example.remove(e)
  else:
    print "Accepting:", e
print "NAIVE:", example 

Removing: 1.0
Accepting: 3.0
Accepting: 4.0
Accepting: 5.0
Accepting: 6.0
NAIVE: [2.0, 3.0, 4.0, 5.0, 6.0]

失败了。我认为它失败了,因为删除列表中的项目会混淆 for 循环 运行 结束的索引,即一旦项目 1. 被删除,项目 2. 就在在列表中放置 0,但到那时,循环位于位置 1。

我可以用 deepcopy 解决这个问题,如下所示:

example = [1.,2.,3.,4.,5.,6.]
import copy
for e in copy.deepcopy(example):
  if e < 3.:
    print "Removing:", e
    example.remove(e)
  else:
    print "Accepting:", e
print "DEEPCOPY:",  example

Removing: 1.0
Removing: 2.0
Accepting: 3.0
Accepting: 4.0
Accepting: 5.0
Accepting: 6.0
DEEPCOPY: [3.0, 4.0, 5.0, 6.0]

这在这里行得通,但这是个好习惯吗?会不会导致其他意想不到的bug?有没有更好的方法来实现这一目标?还是这种结构(循环并从列表中删除)根本不合理?

我不想制作一份新的已接受项目列表,因为我想将一系列标准逐一应用到我的列表中,然后相应地删除项目。我不想为我应用的每个标准(可能有很多)创建一个新列表,我也不想一次应用我的所有标准(因为这有助于查看每个标准删除了多少项目等) .

你是对的,问题是你正在修改循环期间迭代的列表。这是非常不一致的,会导致很多错误。我的问题是,为什么您对删除列表中的项目特别感兴趣,而不是生成符合您建议的新副本?对此有具体要求吗?否则我建议制作一份符合您限制的列表的新副本,而不是修改输入列表。所以,修改你的代码:

example = [1.,2.,3.,4.,5.,6.]
new_list = []
for e in example:
   if e >= 3.:
      new_list.append(e)
      print "Accepting:", e
   else:
      print "Removing: ", e

这不太容易出错,但您可以更像 pythonic 并为此使用列表理解:

new_list = [e for e in example if e >= 3.]

编辑:我看到您想删除项目而不是创建新列表的原因是您要多次浏览列表以过滤列表。我仍然认为,即使在那种情况下,每次创建一个新列表也更具可读性、更不容易出错并且效率也不会特别低。如果效率是个问题并且你有非常大的列表或类似的东西,我会尝试只遍历列表一次并在同一个循环中删除所有无效的项目。但是,如果您真的 想从列表中删除项目,您可以按照@RemcoGerlich 所说的那样做,然后按索引向后迭代。

我不明白你为什么不只用你想保留的项目构建一个新列表,因为你似乎并不关心构建一个新列表(毕竟,这就是 copy 确实如此)。

所以我会做

example = [f for f in example if f >= 3]

如果您确实想要遍历列表并更改它,也许遍历索引 并向后移动:

for i in range(len(example) - 1, -1, -1):
    if example[i] < 3:
        del example[i]

但这有点特别,除非真的有必要,否则我会避免它。

为了表明您不需要愚蠢的 example_1、example_2、old_example 等变量,请考虑:

# Here is a number of tests for things we want throw out
def some_really_complicated_test_on_a_number(f):
    ... put any kind of code here and return True if we want to
    delete the number...

TESTS = (
    lambda f: f < 3,
    lambda f: f > 16,
    lambda f: (int(f) % 2) == 1,  # Integer is odd
    some_really_complicated_test_on_a_number,
    # etc
)

这是一个接受列表和测试的函数,打印带有 "accepting" 和 "rejecting" 的项目,以及 returns 包含剩余项目的新列表:

def filter_with_prints(l, test):
    result = []
    for f in l:
         if test(f):
             print("Rejecting: {}".format(f))
         else:
             result.append(f)
             print("Accepting: {}".format(f))
    return result

我们可以像这样调用很多测试:

example = [1., 2., 3., 4., 5., 6.]

for test in TESTS:
    example = filter_with_prints(example, test)