【问题标题】:Python 3: Removing list item with for loop, is this the right way? [duplicate]Python 3:使用 for 循环删除列表项,这是正确的方法吗? [复制]
【发布时间】:2016-11-11 01:03:24
【问题描述】:

我仍在学习 python 的基础知识,我刚刚花了一段时间阅读有关如何从 for 循环中的 python 列表中删除项目。我读过的所有内容都暗示了执行此操作的复杂方法,他们说在迭代时不能从列表中删除项目。但是...这似乎可行:

class Object():
    def __init__(self):
        self.y = 0

object_list = [Object(), Object(), Object()]

for thing in object_list:
    thing.y += 1
    if thing.y > 10:
        object_list.remove(thing)

当其他人说它不是并且编写复杂的解决方法时,为什么它会起作用?是不是因为你在 Python 2 中不允许这样做,但在 Python 3 中可以这样做?

这是正确的方法吗?它会按我的意愿工作还是容易出现错误?如果我打算删除项目,是否建议以相反的顺序遍历列表?

抱歉,如果之前已经回答过这个问题,但很难知道哪些资源指的是什么,因为它们都只是在标签中说“python”(至少,我一直在阅读的那些,也许是因为所有的我读过的是python 2?)

谢谢!

编辑:

抱歉,出现了一些复制和粘贴错误...我已修复它们...

编辑:

我一直在观看 Raymond Hettinger 的另一个视频...他提到了一种在使用 dict.keys() 迭代字典时从字典中删除项目的方法。比如:

d = {'text': 'moreText', 'other': 'otherText', 'blah': 'moreBlah'}

for k in d.keys():
    if k.startswith('o'):
        del d[k]

显然使用键可以在迭代时安全地删除项目。列表是否有等价物?如果有,我可以向后遍历列表并安全删除项目

【问题讨论】:

  • 它不起作用。你的对象简直是不可区分的;所以你在移除后无法区分。尝试将self 添加到__init__ 并使用不同的y 值进行初始化。显然,if 分支从未被占用
  • 你确定这有效吗?在我看来,您会在for thing in thing_list: 获得NameError
  • 好吧,对象最终会有不同的 y 值,但我想确保如果两个对象由于某种原因碰巧具有相同的 y 值,那么它们都将被删除。我猜当您从列表中删除一个对象时,它会将其余部分移到一个位置,因此迭代器意外地跳转了一个对象,同时仍在索引上?
  • 它工作正常,名称错误是什么?我刚刚看到,如果我将 9 个项目添加到列表中,在第一次通过 for 循环时,会丢失 2 个项目,此时应该将所有项目都删除。然后有 1 离开了下一次运行,然后最后没有。我很惊讶它没有抱怨中途缺少物品?
  • 啊,公平地说,我确实有 __init__(self)。刚才写错了,抱歉,我修改一下

标签: python list python-3.x for-loop


【解决方案1】:

这里有一些例子

def example1(lst):
    for item in lst:
        if item < 4:
            lst.remove(item) 
    return lst

def example2(lst):
    for item in lst[:]:
        if item < 4:
            lst.remove(item)       
    return lst

def example3(lst):
    i = 0
    while i < len(lst):
        if lst[i] < 4:
            lst.pop(i)
        else:
            i += 1
    return lst

def example4(lst):
    return [item for item in lst if not item < 4]

def example5(lst):
    for item in reversed(lst):
        if item < 4:
            lst.remove(item)
    return lst

def example6(lst):
    for i, item in reversed(list(enumerate(lst))):
        if item < 4:
            lst.pop(i)
    return lst

def example7(lst):
    size = len(lst) - 1
    for i, item in enumerate(reversed(lst)):
        if item < 4:
            lst.pop(size - i)
    return lst

def example8(lst):
    return list(filter(lambda item: not item < 4, lst))

import itertools
def example9(lst):
    return list(itertools.filterfalse(lambda item: item < 4, lst))

# Output
>>> lst = [1, 1, 2, 3, 2, 3, 4, 5, 6, 6]
>>> example1(lst[:])
[1, 3, 3, 4, 5, 6, 6]
>>> example2(lst[:])
[4, 5, 6, 6]
>>> example3(lst[:])
[4, 5, 6, 6]
>>> example4(lst[:])
[4, 5, 6, 6]
>>> example5(lst[:])
[4, 5, 6, 6]
>>> example6(lst[:])
[4, 5, 6, 6]
>>> example7(lst[:])
[4, 5, 6, 6]
>>> example8(lst[:])
[4, 5, 6, 6]
>>> example9(lst[:])
[4, 5, 6, 6]

示例 1 此示例涉及遍历列表并从中删除值。这样做的问题是您在遍历列表时正在修改列表,因此您的列表在迭代期间会发生变化,因此某些元素会被跳过。

示例 2 在这里,我们迭代列表的浅表副本而不是列表本身。这样做的问题是,如果您的列表很大,这样做可能会很昂贵。

示例 3 以下是使用pop 而不是remove 的示例,remove 的问题是它会从列表中删除它找到的值的第一个实例。除非您有相等的对象,否则这通常没有问题。 (见示例 10)

示例 4 我们不在这里修改列表,而是使用列表推导创建一个新列表,只允许指定值。

示例 5 这是一个反向遍历列表的示例,不同之处在于我们使用内置的reversed 函数来应用for循环而不是带有计数器的while循环。

示例 6 类似的例子使用pop 代替。

示例 7 使用pop 的更好示例,因为我们不必转换回列表即可使用reversed 函数。

示例 8 使用内置过滤器方法删除指定值的示例。

示例 9 使用来自itertoolsfilerfalse 方法的类似示例

class Example(object):
    ID = 0
    def __init__(self, x):
        self._x = x
        self._id = str(Example.ID)
        Example.ID += 1

    def __eq__(self, other):
        return self._x == other._x

    def __repr__(self):
        return 'Example({})'.format(self._id)

def example10():
    lst = [Example(5), Example(5)]
    print(lst)
    lst.remove(lst[1])
    return lst

#Output
>>> example10()
[Example(0), Example(1)]
[Example(1)]

示例 10 在这里,我们创建了两个具有相同值的 Example 对象,并且通过相等方法它们相等。 ID 变量可以帮助我们区分两者。现在我们已经指定要从列表中删除第二个对象,但是因为两者相等,所以实际上删除了第一项。

时间安排 这些是相当艰难的时期,可能会因您的设备而略有不同。尽管这些可以确定哪个更快,但这是使用包含 10,000 个项目的列表进行测试的,因此如果您没有任何接近的项目,那么任何选择都可以。

import timeit
import random

# Code from above is here

def test(func_name):
    global test_lst
    test_lst = lst[:]
    return timeit.timeit("{}(test_lst)".format(func_name),
                         setup="from __main__ import {}, test_lst".format(func_name), number = 1)

if __name__ == '__main__':
    NUM_TRIALS = 1000
    lst = list(range(10000))
    random.shuffle(lst) # Don't have to but makes it a bit interesting
    test_list = lst[:]

    for func in ('example2', 'example3', 'example4', 'example5',
                 'example6', 'example7', 'example8', 'example9'):
        trials = []
        for _ in range(NUM_TRIALS):
            trials.append(test(func))
        print(func, sum(trials) / len(trials) * 10000)

#Output
example2 8.487979147454494
example3 20.407155912623292
example4 5.4595031069025035
example5 7.945100572479213
example6 14.43537688078149
example7 9.088818018676008
example8 14.898256300967116
example9 13.865010859443247

【讨论】:

  • 谢谢!这是一组有用的示例。如果我使用大型列表并且只希望删除几个项目,示例 4 是否也会很慢?
  • 它与示例 3 大致相同,并且比示例 2 更好。
  • 好的,干杯!向后迭代呢?我知道建议不要在迭代对象时修改它们,但如果我所做的只是删除它们,那么如果我以相反的顺序迭代是否重要?我的测试表明它确实从我的列表中删除了它应该删除的所有对象
  • 问题是您仍然需要遍历列表以检查每个值,因此您的最佳解决方案将是一个需要单个循环的解决方案。至于向后迭代它没有问题,但它仍然在迭代期间修改列表,所以我不能保证它的可靠性。无论如何,实际上没有必要向后迭代它。正在添加示例。
  • 好的,我已经为提供的每个示例添加了时间,看起来我已经离开了示例 3。可能是由于检查和删除项目的索引。 6、7、8 是预期的,因为我们必须将其转换回列表,但我想我还是会添加它们。
【解决方案2】:

它会起作用的。但是,在迭代对象时修改对象绝不是一个好主意。您可能会遇到意想不到的行为。

如果我这样做:

my_list = [1, 2, 3, 4]
for x in my_list:
    if x+1 in my_list:
        my_list.remove(x+1)

我希望最后是my_list = [1]。 1 删除 2,2 删除 3,3 删除 4。如果我检查我找到 my_list=[1,3]。这是因为在第一次循环中2从列表中被删除了,所以第二次循环使用3删除了4,而3仍然在列表中。

【讨论】:

  • 那么正确的解决方法是什么?它漂亮吗?我一直在看 Raymond Hettinger 关于漂亮代码的视频,似乎开发团队喜欢为这类问题制作漂亮且易于使用的快捷方式
  • 完全取决于您想要实现的目标。例如,从for 循环中的列表中删除项目没有任何问题,如果这不是您正在迭代的对象。如果是,请创建一个空列表,然后 append 您要删除到该列表的每个对象。然后,您可以遍历第二个列表,并从第一个列表中删除每个项目。
  • 如果我向后循环浏览列表会不会安全,这样我就不会在找到它们之前弄乱项目的顺序
  • 带有“if”过滤器的列表/生成器表达式可能会做到这一点。如果过滤条件很复杂,你可以把它放在一个函数中
  • 向后循环列表并没有真正改变任何东西。根本问题还是一样的;您在迭代对象时正在修改它。对于它的价值,我是漂亮代码的忠实粉丝。然而,它比你的代码更重要,并且是健壮的。
猜你喜欢
  • 1970-01-01
  • 2016-04-07
  • 1970-01-01
  • 1970-01-01
  • 2016-07-30
  • 2017-04-17
  • 1970-01-01
  • 2011-07-21
  • 2011-01-04
相关资源
最近更新 更多