Python切片列表中的第一个和最后一个元素

99

有没有一种方法可以仅切割列表中的第一个和最后一个元素?

例如,如果这是我的列表:

>>> some_list
['1', 'B', '3', 'D', '5', 'F']

做这个(显然[0,-1]不是有效的语法):

>>> first_item, last_item = some_list[0,-1]
>>> print first_item
'1'
>>> print last_item
'F'

我尝试过的一些方法:

In [3]: some_list[::-1]
Out[3]: ['F', '5', 'D', '3', 'B', '1']

In [4]: some_list[-1:1:-1]
Out[4]: ['F', '5', 'D', '3']

In [5]: some_list[0:-1:-1]
Out[5]: []
...

6
哈哈,3个答案,在2秒内一模一样,其中一个是你的。经典。 - Aesthete
3
first, last = some_list[0], some_list[-1] 有什么不好的地方? - Matthew Adams
@MatthewAdams 因为我是在同一行分割它,这样就需要花费两倍的时间进行分割:x, y = a.split("-")[0], a.split("-")[-1] - chown
但实际上,无论如何我都要先获取列表的长度,所以我可能最终需要这样做。 - chown
3
就翻译而言,原文意思是作者在代码审查中拒绝使用some_list[0::len(some_list)-1],认为这样太聪明反而不好。 - DSM
2
@chown: 但是,按照你的解决方案,步骤集设置为len-1,你仍然需要再次拆分才能获取长度! - Martijn Pieters
13个回答

129

一种方法:

some_list[::len(some_list)-1]

更好的方法(不使用切片,但更易于阅读):

[some_list[0], some_list[-1]]

27
第二种形式更易读。显式比隐式再次更好。 - Martijn Pieters
4
因此,“可能更好的方法”是这样的。我之所以没有将其放在第一位,是因为问题明确要求“切片”,而第二种形式在技术上不是一个切片…… - mgilson
4
向 OP 传授他错误方式的愚蠢之处永远都不会太晚。:-P - Martijn Pieters
2
如果some_list中只有一个项目,则切片形式会出现“ValueError:slice step cannot be zero”的错误。 - rickfoosusa
1
@rickfoosusa -- 嗯...我想这取决于期望的输出。你说得对,它不会给你2个项目作为结果,这在许多应用程序中可能被认为是一个错误...“更好”的方法会给你相同的项目两次,这可能同样糟糕,具体取决于应用程序... - mgilson
显示剩余3条评论

43

Python 3 的答案(不使用切片或丢弃列表的其余部分,但可能已足够好)是使用拆包泛化从中间获取 firstlast

first, *_, last = some_list

_这个名字被用来表示“我不关心的东西”,作为“其余”参数的通配符是任意选择的。

与许多其他解决方案不同的是,此解决方案将确保序列中至少有两个元素;如果只有一个元素(因此firstlast相同),它将引发异常(ValueError)。

注意:由于它必须将所有其他元素收集到_中,因此对于已经是序列的大型输入来说,这是不合适的,会做出不必要的工作(O(n)与直接索引或切片方法的O(1)相比)。作为交换,在错误检查之上,它可以处理任何可迭代输入,而不仅仅是listtuple和其他序列类型。


5
我很惊讶这篇文章没有得到更多的点赞。这是迄今为止最优雅的解决方案,而且避免了多次引用列表。非常适合像“打开文件-获取第一行-拆分-获取第一个和最后一个元素”这样的任务(这也是我来这里的原因)。 - nicolaum
6
这个解决方案的时间复杂度也是O(N),而其他许多解决方案的时间复杂度是O(1)。这是否重要很大程度上取决于具体应用的情况 :)。 - mgilson
1
@mgilson:没错,这是使用任意可迭代对象(包括迭代器)的必然结果;你不能在不到达结尾的情况下结束。 :-) - ShadowRanger
1
@ferreiradev:公平地说,对于任意可迭代情况(这种情况下不需要更改),您无法在O(n)以下完成它。对于已经是序列的较长输入,它的性能比其他方法差。作为交换,它在较短的输入上具有(稍微)更好的性能,执行隐式错误检查,并适用于任何可迭代输入。 "更差"是一个夸张的说法。 - ShadowRanger
花了一些时间来理解你的观点,我承认当你考虑到不是序列类型(如dict和set)的对象时,你不能在O(n)以下完成它,这值得注意。谢谢。有一些细微差别在单词“sequence”、“list”和“iterable”中,读者必须注意以理解你的答案,特别是那些期望仅限于列表(而不是字典、集合、迭代器)的答案的读者。 - ferreiradev
显示剩余2条评论

21

我只是想展示如何使用 numpy 的高级索引来完成这个操作:

>>> import numpy
>>> some_list = ['1', 'B', '3', 'D', '5', 'F']
>>> numpy.array(some_list)[[0,-1]]
array(['1', 'F'], 
      dtype='|S1')

注意,它还支持任意索引位置,而[::len(some_list)-1]方法无法使用:

>>> numpy.array(some_list)[[0,2,-1]]
array(['1', '3', 'F'], 
      dtype='|S1')

正如DSM指出的那样,您可以使用itemgetter做类似的事情:

>>> import operator
>>> operator.itemgetter(0, 2, -1)(some_list)
('1', '3', 'F')

1
NumPy通常让我感到开心! - jterrace
6
你可以使用itemgetter而不需要numpy来实现这个变量:itemgetter(0, -1)(some_list) - DSM

17
first, last = some_list[0], some_list[-1]

9

有些人似乎回答错了问题。你说你想要做的是:

>>> first_item, last_item = some_list[0,-1]
>>> print first_item
'1'
>>> print last_item
'F'

例如,您想将第一个和最后一个元素分别提取到单独的变量中。

在这种情况下,Matthew Adams、pemistahl和katrielalex的答案都是有效的。这只是一个复合赋值:

first_item, last_item = some_list[0], some_list[-1]

但是后来你提到了一个问题:“我要在同一行中分割它,这将需要花费两次分割的时间:”

x, y = a.split("-")[0], a.split("-")[-1]

因此,为了避免两个split()调用,您必须仅对从一次拆分中得到的列表进行操作。

在这种情况下,试图在一行中完成太多操作会影响清晰度和简洁性。使用变量来保存拆分结果:

lst = a.split("-")
first_item, last_item = lst[0], lst[-1]

其他回答已经回答了“如何获得一个由列表的第一个和最后一个元素组成的新列表”的问题?他们可能受到您的标题的启发,该标题提到了切片,但根据仔细阅读您的问题,您实际上并不需要切片。

据我所知,有三种方法可以获得一个由列表的第0个和最后一个元素组成的新列表:

>>> s = 'Python ver. 3.4'
>>> a = s.split()
>>> a
['Python', 'ver.', '3.4']

>>> [ a[0], a[-1] ]        # mentioned above
['Python', '3.4']

>>> a[::len(a)-1]          # also mentioned above
['Python', '3.4']

>>> [ a[e] for e in (0,-1) ] # list comprehension, nobody mentioned?
['Python', '3.4']

# Or, if you insist on doing it in one line:
>>> [ s.split()[e] for e in (0,-1) ]
['Python', '3.4']

列表推导式的优点是,元组中的索引集可以是任意的,并且可以通过编程方式生成。

只是提醒一下,列表推导式方法的“任意和程序生成”的索引优势与a solution using operator.itemgetter共享;当你构造它时,itemgetter需要查找任意数量的东西,并在你在集合上调用结果时将它们全部检索为单个元组。通常比列表推导式运行得更快,特别是如果itemgetter被预先构建并重复使用,尽管它的弱点是如果只请求一个项目,则不会将结果作为元组。 - ShadowRanger

7
您可以像这样做:
some_list[0::len(some_list)-1]

7
这个怎么样?
>>> first_element, last_element = some_list[0], some_list[-1]

6
你可以使用类似以下内容的方法:
y[::max(1, len(y)-1)]

如果你真的想要使用切片。这样做的好处是它不会出现索引错误,并且也可以处理长度为1或0的列表。


4

您觉得这个怎么样?

some_list[:1] + some_list[-1:]

Result: ['1', 'F']

4

实际上,我刚刚弄明白了:

In [20]: some_list[::len(some_list) - 1]
Out[20]: ['1', 'F']

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