在Python中以相反的顺序遍历列表

1052

如何在Python中反向遍历列表? 这样我就可以从collection[len(collection)-1]开始,以collection[0]结束。

我还想能够访问循环索引。

28个回答

1716
使用内置的reversed()函数:

使用内置的reversed()函数:

>>> a = ["foo", "bar", "baz"]
>>> for i in reversed(a):
...     print(i)
... 
baz
bar
foo

如果还需要访问原始索引,请在将列表传递给 reversed() 之前,使用enumerate()对其进行处理:

>>> for i, e in reversed(list(enumerate(a))):
...     print(i, e)
... 
2 baz
1 bar
0 foo

由于enumerate()返回的是生成器,而生成器无法反转,因此您需要先将其转换为list


184
在遍历时不会创建副本,元素会在遍历中被反转!这是所有这些迭代函数(它们都以“ed”结尾)的一个重要特性。 - Konrad Rudolph
12
不,这是对原始数据的迭代器,不会创建副本! - André
128
为避免混淆,请注意:reversed()不会修改列表。 reversed()也不会创建列表的副本(否则它将需要额外的O(N)内存)。如果你想要修改列表,请使用alist.reverse();如果你需要一个反转顺序后的列表副本,请使用alist[::-1] - jfs
149
在这个答案中,使用list(enumerate(a))确实会创建一个副本。 - Kenan Banks
65
@ JF,reversed() 不会创建一个副本,但是 list(enumerate()) 会创建一个副本。 - Kenan Banks
显示剩余19条评论

253

你可以这样做:

for item in my_list[::-1]:
    print item

(在for循环中,您可以执行任何想要执行的操作。)

[::-1] 切片会在for循环中反转列表(但不会永久地修改您的列表)。


39
[::-1] 创建一个浅拷贝,因此它既不会“永久”地也不会“临时”地改变数组。 - jfs
6
在Python 2.7下(已测试),这比使用reversed稍微慢一些。 - kgriffs
23
这个答案的工作原理是:使用以下参数创建列表的切片副本:起始点 :未指定(变成列表长度,因此从末尾开始),结束点 :未指定(变成某个魔法数字,不是0,可能是-1),因此在开头结束),以及步长-1(从列表的末尾向后迭代,每次迭代一个项目)。 - Edward
2
我也测试了这个(Python 2.7),使用 [::-1] 比使用 reversed() 慢了约10%。 - RustyShackleford

123

可以这样做:

for i in range(len(collection)-1, -1, -1):
    print collection[i]

    # print(collection[i]) for python 3. +

你的猜测很接近:) 稍微有点别扭,但基本上是这样说的:从比 len(collection) 少 1 开始,以步长 -1 继续,直到 -1 前结束。

顺便说一下,help 函数非常有用,它可以让你从 Python 控制台查看某个东西的文档,例如:

help(range)


1
对于 Python 3.0 之前的版本,我认为在集合长度较大时,xrange 比 range 更可取。 - Brian M. Hunt
我相信你是正确的 :) 如果我没记错,range() 会生成整个范围作为一个数组,但xrange() 返回一个迭代器,只在需要时生成值。 - Alan Rowarth
18
这看起来有太多的“-1”,感觉很奇怪。我建议使用“reversed(xrange(len(collection)))”。 - musiphil
1
感谢您对参数值的解释,我也喜欢Barney Szabolcs的解决方案,它基于相同的反向范围迭代器。您的解释使其易于理解。 - Alex Stoneham
1
for i in range(start, end, width):注意:当 start 包含在内时,end 不包含在内。例如,如果 width=-1(用于反向迭代),并且 start=len(collection)-1,则 end=-1 将帮助迭代到集合的第一个元素(索引为0)。end=0 将帮助反向迭代到集合的索引1(因为索引0应该被排除在外)。 - KNU
显示剩余3条评论

80

如果您需要循环索引,并且不想遍历整个列表两次或使用额外的内存,我建议编写一个生成器。

def reverse_enum(L):
   for index in reversed(xrange(len(L))):
      yield index, L[index]

L = ['foo', 'bar', 'bas']
for index, item in reverse_enum(L):
   print index, item

3
我会将该函数称为“enumerate_reversed”,但这可能只是我的个人喜好。我认为你的答案对于这个具体问题来说是最简洁明了的。 - tzot
1
reversed(xrange(len(L))) 产生的索引与 xrange(len(L)-1, -1, -1) 相同。 - jfs
3
我更喜欢简单的方法来理解:对于L中的每个元素item,倒序地枚举它的下标index,输出len(L)-1-index和item - Don Kirkby
2
@Triptych 我刚刚不得不面对一个事实,即从reversed()枚举不会产生反向索引,而你的代码帮了我很多。这种方法应该在标准库中。 - oski86
2
reversed(xrange()) 之所以有效,是因为 xrange 对象具有 reversed 方法、len 方法和 getitem 方法,而 reversed 可以检测到并使用它们。但是,enumerate 对象没有 reversed__、__lengetitem 方法。但是,“为什么”枚举没有这些方法呢?我不知道。 - FutureNerd
显示剩余4条评论

45

一种不需要导入任何模块的方法:

for i in range(1,len(arr)+1):
    print(arr[-i])

时间复杂度为O(n),空间复杂度为O(1)。

一种在内存中创建新列表的方法,请注意处理大型列表:

for i in arr[::-1]:
    print(i)

时间复杂度为O(n),空间复杂度为O(n)。


7
这个答案应该是最佳答案,第一种方法不会改变列表本身,也没有复制任何元素,我们只是反向遍历索引。速度非常快。第二种方法会创建一个新列表,请注意。 - Amro Younes
1
完美地满足了我所需的功能。谢谢! - Johnny John Boy

41

reversed内置函数很方便:

for item in reversed(sequence):

reversed的文档解释了它的限制。

对于需要逆序遍历序列以及索引(例如,用于更改序列长度的原地修改),我在我的代码工具模块中定义了此函数:

from six.moves import zip as izip, range as xrange

def reversed_enumerate(sequence):
    return izip(
        reversed(xrange(len(sequence))),
        reversed(sequence),
    )

这个方法避免了创建序列的副本。显然,reversed 的限制仍然适用。


16

此外,你可以使用"range"或者"count"函数,如下所示:

a = ["foo", "bar", "baz"]
for i in range(len(a)-1, -1, -1):
    print(i, a[i])

3 baz
2 bar
1 foo

您可以使用itertools中的“count”函数,如下所示:

您可以使用itertools中的“count”函数:

a = ["foo", "bar", "baz"]
from itertools import count, takewhile

def larger_than_0(x):
    return x > 0

for x in takewhile(larger_than_0, count(3, -1)):
    print(x, a[x-1])

3 baz
2 bar
1 foo

你第一个代码块中的代码没有产生正确的输出;实际上输出应该是 3 foo\n2 bar\n1 baz - amiller27
为了避免在第一个示例中使用"a[i-1]",可以使用这个范围"range(len(a)-1, -1, -1)"。这更加简化了。 - Francisc

14

在Python 3中,list会创建一个副本,因此reversed(list(enumerate(collection)))可能效率低下,生成另一个列表没有被优化。

如果collection确实是一个列表,那么最好将复杂性隐藏在迭代器后面。

def reversed_enumerate(collection: list):
    for i in range(len(collection)-1, -1, -1):
        yield i, collection[i]

所以,最干净的方法是:

for i, elem in reversed_enumerate(['foo', 'bar', 'baz']):
    print(i, elem)

1
这让我哭了。 - CervEd
1
@CervEd 你说得完全正确,当我回来时,它也让我哭了。我更新了我的答案。——虽然range函数仍然让我有点难过... - Barney Szabolcs

13

如何在不创建新列表的情况下进行操作,你可以通过索引来实现:

>>> foo = ['1a','2b','3c','4d']
>>> for i in range(len(foo)):
...     print foo[-(i+1)]
...
4d
3c
2b
1a
>>>
>>> length = len(foo)
>>> for i in range(length):
...     print foo[length-i-1]
...
4d
3c
2b
1a
>>>

13
>>> l = ["a","b","c","d"]
>>> l.reverse()
>>> l
['d', 'c', 'b', 'a']
>>> print l[::-1]
['d', 'c', 'b', 'a']

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