移除列表的最后N个元素。

79

有没有更好的方法可以删除列表的最后N个元素?

for i in range(0,n):
    lst.pop( )

6
不要将你的列表命名为 list,否则你将无法使用内置的 list 类。 - jamylak
7个回答

125

适用于 n >= 1

>>> L = [1,2,3, 4, 5]
>>> n=2
>>> del L[-n:]
>>> L
[1, 2, 3]

1
这个问题与其他答案提到的问题相同:它不适用于n <= 0。(0删除列表,<0从列表前面删除,这可能是正确的,但也可能不是)。如果你可以抽出时间复制列表,我认为Luca Citi的切片答案更好,否则你需要检查n > 0。 - Daniel B.
@DanielB。然而,它在n <= 0上不起作用,但它是一个简单的解决方案,可以满足许多用例的需求。还有助于教育新手学习Python。任何人都可以随意使用任何解决方案,所以我们有多种选择是很好的。 - jamylak
2
在我看来,这应该是被接受的答案,它不会创建一个新列表,并且@jamylak正确指出要检查n >= 1。 - swK

49

如果您希望删除最后n个元素,换句话说,保留前len - n个元素:

lst = lst[:len(lst)-n]

注意: 这不是一个内存操作,它会创建一个浅拷贝。


请注意,这实际上是将列表复制到第n个元素,尽管通常不会产生太大的影响。 - jamylak
1
同意,这完全是错误的答案!请看:
lst = range(1, 11) lst [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] n = 5 lst = lst[:n - 1] lst [1, 2, 3, 4]
- Corey O.
@CoreyO。你需要执行lst = lst[:len(lst)-n] - shreyashag
3
我的回答是在你发表评论一年后进行了更正。请查看编辑日期和编辑历史记录。 - Corey O.
如果您想进行内存操作,应该怎么做? - fayssal el ansari

41

正如 Vincenzooo 所说,当 n==0 时,Pythonic 的 lst[:-n] 无法正常工作。

对于所有的 n>=0,以下方法都有效:

lst = lst[:-n or None]

我喜欢这个解决方案,因为它在英语中也很容易理解:“返回省略最后n个元素的切片或没有(如果不需要省略)”。

这个解决方案之所以可行,是因为:

  • x or y 的计算结果为 xx 逻辑上为真时(例如,当它不是 0""FalseNone等),否则为 y。因此,当 n!=0 时,-n or None-n,当 n==0 时,-n or NoneNone
  • 当进行切片操作时,None 等同于省略该值,因此 lst[:None]lst[:] 是相同的 (参见这里)。

正如 @swK 所指出的那样,这个解决方案创建了一个新列表(但除非其他地方引用了旧列表,否则立即丢弃旧列表),而不是直接编辑原始列表。在性能方面,通常这不是一个问题,因为一次性创建一个新列表通常比逐个删除一个元素要快(除非 n<<len(lst))。在空间方面也通常不是问题,因为通常列表的成员所占的空间比列表本身更大(除非它是由像bytes这样的小对象组成的列表,或者该列表具有许多重复的条目)。请注意,这个解决方案与原始帖子中的解决方案并不完全相同。如果其他变量引用了原始列表,则此解决方案不会修改(缩短)其他副本。

一个可能的解决方案(与我的原始解决方案相同风格),适用于 n>=0 但: a) 不创建列表的副本; b) 同时影响对同一列表的其他引用,可以是以下内容:

    lst[-n:n and None] = []

这肯定不可读,不应该使用。实际上,即使是我的原始解决方案,也需要对语言的理解过多,才能被每个人快速阅读并明确理解。我不会在任何真正的代码中使用其中任何一个,我认为最好的解决方案是由@wonder.mice提出的:a[len(a)-n:] = []


2
这个解决方案是迄今为止最符合Pythonic和PEP8规范的。它非常棒,因为它考虑到了0的情况。然而,为了按照问题的要求回答,您可能需要将您的答案更改为类似于以下赋值的形式:"lst = lst [:-n or None]" - Corey O.
这个答案是错误的,因为它创建了一个新列表,这与执行多个.pop()不等同(除了性能之外,如果您有另一个指向相同列表的变量,您将失去它!) - swK
1
@swK 你的意思是,如果另一个变量指向原始的 lst,在运行上述代码后,它仍将指向旧的 lst,而不是包含 n 个较少元素的新浅拷贝(这可能是预期的行为,也可能不是)。 - jamylak
1
@jamylak 没错! - swK
@swK 你说得对,这并不完全等同。但我认为在大多数情况下它解决了同样的问题。就性能而言,我想你是指它更慢,但实际上,在大多数情况下它更快(一些快速的非科学测试:对于len(lst)=10000,当n>=20时它更快;对于len(lst)=100,当n>=2时它更快)。创建额外的列表通常不是问题,因为在大多数情况下,列出的对象所占用的空间比列表本身要多得多(除非你有一个字节列表或许多重复的条目)。你关于其他引用的影响是绝对正确的,我会添加一个注释。 - Luca Citi
@LucaCiti 我同意在大多数情况下它确实解决了相同的问题。但对于那些经验不太丰富的人来说,它可能是微妙且难以发现错误的源头。就性能而言,我想这很大程度上取决于底层解释器的工作方式及其优化,但通常复制数组是一个O(n)操作,而缩短数组可以实现O(1),这在数组大小增长时会产生巨大的差异(如果你感觉像测试并且有足够的内存,请尝试使用几十亿个元素的列表)。谢谢提醒,看起来不错! - swK

11

我看到这个问题早就被问过了,但是没有一个答案能够满足我的需求。如果我们想要获取原始列表中除最后N个元素外的所有元素,可以使用以下代码:list[:-n]。如果需要处理n可能为0的情况,则可以使用list[:-n or None]

>>> a = [1,2,3,4,5,6,7]
>>> b = a[:-4]
>>> b
[1, 2, 3]
>>> a
[1, 1, 2, 3, 4, 5, 7]

就是这么简单。


1
没有其他答案能够为您解决问题,因为您的解决方案并不是问题的答案。;-) 复制 a 的一部分并不等同于删除 a 的一部分。 - HeinzKurt

11

就试着像这样删除它。

del list[-n:]

这是比 lst = lst[:len(lst)-n] 更好的方法吗? - zheyuanWang
1
@zheyuanWang 是的,因为它不会在内存中制作列表的浅拷贝。但是,请注意需要 n > 0,因为 n = 0 将删除整个列表。如果您需要支持 n >= 0,请参阅 Luca Citi 的答案。 - Eli

10
应该使用这个:
a[len(a)-n:] = []

或者这样:

del a[len(a)-n:]

这种方式更快,因为它实际上是从现有数组中删除项。相反地 (a = a[:len(a)-1]) 则创建一个新的列表对象,效率较低。

>>> timeit.timeit("a = a[:len(a)-1]\na.append(1)", setup="a=range(100)", number=10000000)
6.833014965057373
>>> timeit.timeit("a[len(a)-1:] = []\na.append(1)", setup="a=range(100)", number=10000000)
2.0737061500549316
>>> timeit.timeit("a[-1:] = []\na.append(1)", setup="a=range(100)", number=10000000)
1.507638931274414
>>> timeit.timeit("del a[-1:]\na.append(1)", setup="a=range(100)", number=10000000)
1.2029790878295898
如果0<n,可以使用a[-n:] = []del a[-n:],这甚至更快。

1
这是一个情况,在其中“pythonic”方式对我不起作用,可能会导致隐藏的错误或混乱。 以上解决方案都无法解决n=0的情况。 在一般情况下使用“l[:len(l)-n]”可以解决问题:
l=range(4)
for n in [2,1,0]: #test values for numbers of points to cut
    print n,l[:len(l)-n]

这对于函数内部修剪向量边缘非常有用,您可以选择不剪切任何内容。

你说以上的解决方案都不适用于 n=0,但被接受的答案与你的解决方案相同。 - jamylak
1
当我写下我的答案时,情况并非如此。被接受的答案在我的之后进行了编辑,如果我对改进原始答案有所贡献,我会感到高兴。 - Vincenzooo
啊,好的,我明白了! - jamylak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接