【问题标题】:Best way to create a "reversed" list in Python?在 Python 中创建“反向”列表的最佳方法是什么?
【发布时间】:2011-04-11 23:08:45
【问题描述】:

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

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

new_list = list(reversed(old_list))

也可以复制old_list,然后原地反转复制:

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

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

【问题讨论】:

    标签: python reverse html-lists


    【解决方案1】:
    newlist = oldlist[::-1]
    

    [::-1] 切片(我的妻子安娜喜欢称之为“火星笑脸”;-) 的意思是:切片整个序列,步长为 -1,即反向。它适用于所有序列。

    请注意,这(您提到的替代方案)等效于“浅拷贝”,即:如果项目是可变的并且您在它们上调用突变器,则保留在项目中的突变原始列表也在反向列表中的项目中,反之亦然。如果您需要避免这种情况,一个copy.deepcopy(虽然总是一个潜在的高成本操作),在这种情况下是一个.reverse,是唯一的好选择。

    【讨论】:

    • 天哪!这优雅的。直到几天前,我才意识到切片时可以包含“步骤”;现在我想知道没有它我是怎么过的!谢谢,亚历克斯。 :)
    • 也感谢您提到这会产生浅拷贝。不过,这就是我所需要的,所以我将在我的代码中添加火星笑脸。
    • 这确实比最初的建议list(reversed(oldlist)) 看起来更神奇,可读性也更差。除了轻微的微优化之外,还有什么理由更喜欢[::-1] 而不是reversed()
    • @BrianCampbell:它有什么“魔力”?如果您完全了解切片,那是完全有道理的。如果你不了解切片……那么,你真的应该在 Python 职业生涯的早期学习它。当然,reversed 在您不需要 需要列表时具有巨大优势,因为它不会浪费内存或构建它的前期时间。但是当您确实需要该列表时,使用[::-1] 代替list(reversed()) 类似于使用[listcomp] 代替list(genexpr)
    • @abarnert 在我使用的代码中,我很少看到第三个切片参数,如果我看到它的用法,我必须查找它的含义。一旦我这样做了,当步长为负时,默认的开始值和结束值交换仍然不明显。快速浏览一下,如果不查找第三个参数的含义,我可能会猜测[::-1] 意味着删除列表的最后一个元素,而不是反转它。 reversed(list) 确切地说明了它在做什么;它阐明了它的意图,在“显式优于隐式”、“可读性很重要”和“稀疏优于密集”的意义上。
    【解决方案2】:

    现在让我们timeit提示: Alex 的 [::-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 建议的list comp 方法。我会让结果说明一切。

    【讨论】:

    • 请注意 - 这对于创建反向副本列表是准确的,但对于迭代而言,反向仍然比 [::-1] 更有效
    【解决方案3】:

    调整

    值得为 sdolan 的 timeit 计算提供基准基准/调整,它显示了“反转”的性能,而没有经常不必要的 list() 转换。这个list() 操作为运行时增加了额外的 26 毫秒,并且仅在迭代器不可接受的情况下才需要。

    结果:

    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 微秒

    【讨论】:

    • reversed() 返回一个惰性求值的迭代器对象,所以我认为它与切片符号 [::-1] 的一般比较不公平。
    • 即使在可以直接使用迭代器的情况下,比如''.join(reversed(['1','2','3'])),切片方法仍然快>30%。
    • 难怪你从前 2 个测试中得到相同的结果:它们是相同的!
    • 我的结果更接近这个。 ol[::-1] 大约是 list(reversed(ol)) 的两倍。 ol[::-1] 方法需要更少的字符来编写。但是,list(reversed(ol)) 对于初学者 python 程序员来说可能更具可读性,并且在我的机器上速度更快。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-06-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-07
    • 1970-01-01
    相关资源
    最近更新 更多