【问题标题】:Looping over list and removing entries in Python在 Python 中循环列表并删除条目
【发布时间】:2015-10-12 01:48:37
【问题描述】:

我想在 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]

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

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

【问题讨论】:

  • 列表中的值总是浮点数还是数字?或者它们也可以是可变对象?此外,refinements 是仅删除还是对列表元素也进行了其他类型的更改?
  • 好问题。在我的全部工作中,该列表将是一个可变对象列表。改进是进一步删除和可能的其他更改

标签: python list for-loop deep-copy


【解决方案1】:

我不明白你为什么不只是用你想要保留的项目构建一个新列表,因为你似乎并不关心构建一个新列表(毕竟,这就是 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
)

这是一个函数,它接受一个列表和一个测试,打印带有“接受”和“拒绝”的项目,并返回一个包含剩余项目的新列表:

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)

【讨论】:

  • 第二个建议很有趣,但它比 deepcopy 解决方案更好吗?
  • 好吧,它不必复制。我不明白你为什么会使用 deepcopy,我仍然不明白为什么构造一个新列表是个问题。
  • 另一种解决方案:保持一个相同长度的列表布尔值deleted_items',如果相应的项被删除,则将相关的布尔值设置为True。
  • 假设我想从列表中删除 20 个系列? new_list_1,new_list_2,... new_list_20?或 old_list = new_list, new_list = [] 每个条件之间。这两个我都不喜欢
  • 不,每次都用同一个名字,就像我在这里做的那样。
【解决方案2】:

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

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

这不太容易出错,但您可以更 Python 并为此使用列表推导:

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

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

【讨论】:

  • 我不想创建新列表 - 这是问题 v1 的开头。如果我想为很多选择多次执行此操作,我会选择 new_list_1new_list_2 等,或者必须做一些 old_list = new_list, new_list = [] 的事情,这两个看起来都不是很好
  • 你可以这样做:example = [e for e in example if e &gt;= 3.],然后是example = [e for e in example if any_criterion]。这样,您将避免临时变量名称,并且在您的代码中看起来更清晰。这与从列表中删除项目非常相似,尽管您每次都会制作一份副本。
  • 列表推导可以在不创建临时列表的情况下完成这项工作。即 >>> 示例 = [1,2,3,4,5,6,7,8] >>> 示例 = [e for e in example if e > 3] >>> 示例 [4, 5, 6 , 7, 8]
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-01-08
  • 1970-01-01
  • 1970-01-01
  • 2021-11-28
  • 1970-01-01
  • 2021-05-02
相关资源
最近更新 更多