首页 > 解决方案 > 遍历列表和删除元素的最佳方法是什么?我目前的做法会导致元素被跳过

问题描述

我正在使用这种方法,但是 for 循环跳过了元素。调试后知道为什么跳过了,所以想知道有没有更好的方法或者合适的方法。

这是我的代码:

class Birthday:
    name = ''
    date = ''


def __init__(self, name, date):
    self.name = name
    self.date = date

...


   dt1 = datetime.datetime.utcfromtimestamp(1428916628.0)  # Year: 2015
   dt2 = datetime.datetime.utcfromtimestamp(1328916628.0)  # Year: 2012
   dt3 = datetime.datetime.utcfromtimestamp(1228916628.0)  # Year: 2008
   dt4 = datetime.datetime.utcfromtimestamp(1128916628.0)  # Year: 2005
   dt5 = datetime.datetime.utcfromtimestamp(1028916628.0)  # Year: 2002

   b1 = Birthday('John', dt1)
   b2 = Birthday('Larry', dt2)
   b3 = Birthday('David', dt3)
   b4 = Birthday('Joe', dt4)
   b5 = Birthday('Jerry', dt5)

   # Elements are mixed on purpose
   dt_list = [b3, b1, b5, b4, b2]

   # Sort the order of elements by date
   dt_list.sort(key=lambda y: y.date)

   for x in dt_list:
       dt_list.remove(x)
       if len(dt_list) <= 3:
           break

Expected: 2008, 2012, 2015
Result: 2005, 2012, 2015

我正在考虑将其添加到 for 循环的第一个换行符:

x = dt_list[0]

但是感觉不太对。

标签: pythonpython-3.x

解决方案


迭代期间的变异是不好的

首先,对您正在迭代的列表进行变异很少是一个好主意。特别是,这是您当前问题的根源。让我们看一个类似的例子。

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

print(l) # [2, 4]

遍历一个列表将返回l[0], l[1], l[2]... 等等,直到它到达列表的末尾。特别是,这意味着如果索引更改,您可能会跳过一些元素。这就是这里发生的事情。

如果你想改变你的列表,你应该这样做。

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

del l[:-3]

print(l) # [3, 4, 5]

虽然,变异数据并不比创建新列表更省时。当您从列表中删除一个元素时,下一个元素需要向左移动,这很昂贵。上面的操作是O(n),其中n是列表的长度。

在这种情况下,创建一个新列表实际上效率更高。

切片

对列表进行切片会返回一个新列表,并且是O(k),其中k是切片的大小。因此,恢复最后三个元素实际上是在恒定时间内运行的。

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

new_l = l[-3:]

print(new_l) # [3, 4, 5]

此外,由于该list.__getitem__函数是用 C 编写的,因此与 for 循环相比,它的速度非常快。

这是只获取最后三个元素的最快方法。


推荐阅读