在Python中创建“反向”列表的最佳方法?


89

在Python中,创建一个与其他列表的项目相同但顺序相反的新列表的最佳方法是什么?(我不想修改现有列表。)

这是我想到的一种解决方案:

new_list = list(reversed(old_list))

也可以复制old_list然后在原处反向复制:

new_list = list(old_list) # or `new_list = old_list[:]`
new_list.reverse()

有没有我忽略的更好的选择?如果不是,是否有令人信服的理由(例如效率)来使用上述方法中的一种?

Answers:


205
newlist = oldlist[::-1]

[::-1]切片(其中我的妻子安娜喜欢称之为“火星笑脸” ;-)方式:切片的整个序列,为-1的步骤,即逆转。它适用于所有序列。

请注意,此代码(以及您提到的替代方法)等效于“浅表副本”,即:如果项目是可变的,并且您在其上调用了mutators,则原始列表中包含的项目中的突变也位于反向列表,反之亦然。如果您需要避免copy.deepcopy这种情况.reverse,那么唯一的选择是a (虽然总是可能是昂贵的操作),然后再加上a 。


天啊!这优雅的。直到几天前,我才意识到切片时可以包含一个“步骤”。现在,我想知道如果没有它我该怎么过!谢谢,亚历克斯。:)
davidchambers 2010年

1
也要感谢您提到这会产生浅表副本。不过,这就是我所需要的,因此我将在代码中添加一个火星笑脸。
davidchambers,2010年

13
与原来的建议相比,这确实看起来更具魔力,而且可读性差list(reversed(oldlist))。除了小微的优化,没有任何理由,更喜欢[::-1]reversed()
布莱恩·坎贝尔

@BrianCampbell:这是什么“魔术”?如果您完全了解切片,则完全有意义。如果您不懂切片,那么,您真的应该在您的Python生涯的早期就学习它。当然,reversed有一个巨大的优势,当你并不需要在列表中,因为它不浪费内存或先期时间建立它。但是,当您确实需要该列表时,using [::-1]代替list(reversed())类似于使用a [listcomp]代替list(genexpr)
2014年

10
@abarnert在我使用的代码中,我很少看到第三个slice参数,如果我看到它的用法,则必须查找它的含义。一旦执行此操作,当步数为负时,默认的开始值和结束值是否会交换仍然不是很明显。乍一看,如果不查找第三个参数的含义,我可能会认为这[::-1]意味着删除列表的最后一个元素,而不是反转它。reversed(list)准确说明正在做什么;从“显式胜于隐式”,“可读性计数”和“稀疏胜于密集”的意义上讲,它阐明了其意图。
Brian Campbell 2014年

56

现在开始吧timeit提示:亚历克斯[::-1]是最快的:)

$ p -m timeit "ol = [1, 2, 3]; nl = list(reversed(ol))"
100000 loops, best of 3: 2.34 usec per loop

$ p -m timeit "ol = [1, 2, 3]; nl = list(ol); nl.reverse();"
1000000 loops, best of 3: 0.686 usec per loop

$ p -m timeit "ol = [1, 2, 3]; nl = ol[::-1];"
1000000 loops, best of 3: 0.569 usec per loop

$ p -m timeit "ol = [1, 2, 3]; nl = [i for i in reversed(ol)];"
1000000 loops, best of 3: 1.48 usec per loop


$ p -m timeit "ol = [1, 2, 3]*1000; nl = list(reversed(ol))"
10000 loops, best of 3: 44.7 usec per loop

$ p -m timeit "ol = [1, 2, 3]*1000; nl = list(ol); nl.reverse();"
10000 loops, best of 3: 27.2 usec per loop

$ p -m timeit "ol = [1, 2, 3]*1000; nl = ol[::-1];"
10000 loops, best of 3: 24.3 usec per loop

$ p -m timeit "ol = [1, 2, 3]*1000; nl = [i for i in reversed(ol)];"
10000 loops, best of 3: 155 usec per loop

更新:添加了inspectorG4dget建议的列表补偿方法。我让结果说明一切。


8
只需注意-这对于创建反向副本列表是准确的,但反向迭代对于迭代而言仍然更为有效[::-1]
Tadhg McDonald-Jensen

7

调整项

值得为sdolan的时间计算提供基准基准/调整,以显示“反转”的性能,而无需经常进行不必要的list()转换。此list()操作为运行时增加了26个usecs,仅在迭代器不可接受的情况下才需要。

结果:

reversed(lst) -- 11.2 usecs

list(reversed(lst)) -- 37.1 usecs

lst[::-1] -- 23.6 usecs

计算:

# I ran this set of 100000 and came up with 11.2, twice:
python -m timeit "ol = [1, 2, 3]*1000; nl = reversed(ol)"
100000 loops, best of 3: 11.2 usec per loop

# This shows the overhead of list()
python -m timeit "ol = [1, 2, 3]*1000; nl = list(reversed(ol))"
10000 loops, best of 3: 37.1 usec per loop

# This is the result for reverse via -1 step slices
python -m timeit "ol = [1, 2, 3]*1000;nl = ol[::-1]"
10000 loops, best of 3: 23.6 usec per loop

结论:

这些测试的结论reversed()[::-1]12.4 usecs 的切片速度更快


15
reversed()返回一个被延迟评估的迭代器对象,因此我认为它与切片符号[::-1]的比较不公平。
虹彩

1
即使在可以直接使用迭代器的情况下(例如)''.join(reversed(['1','2','3'])),切片方法仍然快30%以上。
dansalmo

1
难怪为什么您从前两个测试中得到相同的结果:它们是相同的!
MestreLion

我的结果与此更相似。ol [::-1]大约是list(reversed(ol))的两倍。ol [::-1]方法需要更少的字符来编写。但是,list(reversed(ol))对于初学者python程序员来说可能更具可读性,并且在我的机器上更快。
dhj 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.