如何反转列表或向后循环列表?

2024-11-25 08:49:00
admin
原创
194
摘要:问题描述:如何在 Python 中反向迭代列表?另请参阅:如何获取列表的反向副本(在 .reverse 后链接方法时避免使用单独的语句)?解决方案 1:要获取新的反向列表,请应用该reversed函数并将项目收集到list:>>> xs = [0, 10, 20, 40] >>&...

问题描述:

如何在 Python 中反向迭代列表?


另请参阅:如何获取列表的反向副本(在 .reverse 后链接方法时避免使用单独的语句)?


解决方案 1:

要获取新的反向列表,请应用该reversed函数并将项目收集到list

>>> xs = [0, 10, 20, 40]
>>> list(reversed(xs))
[40, 20, 10, 0]

要向后迭代列表:

>>> xs = [0, 10, 20, 40]
>>> for x in reversed(xs):
...     print(x)
40
20
10
0

解决方案 2:

>>> xs = [0, 10, 20, 40]
>>> xs[::-1]
[40, 20, 10, 0]

扩展切片语法在此处解释。另请参阅文档。

解决方案 3:

用于就地list.reverse反转列表:

>>> xs = [0, 10, 20, 40]
>>> xs.reverse()
>>> xs
[40, 20, 10, 0]

使用切片来创建一个按相反顺序排列项目的新列表:

>>> xs[::-1]
[40, 20, 10, 0]

解决方案 4:

逆向方法总结

有三种不同的内置方法来反转列表。哪种方法最好取决于您是否需要:

  1. 就地反转现有列表(改变原始列表变量)

    • 最好的解决方案是object.reverse()方法

  2. 创建反向列表的迭代器(因为您要将其提供给 for 循环、生成器等)

    • 最好的解决方案是reversed(object)创建迭代器

  3. 创建列表的副本,但顺序相反(以保留原始列表)

    • 最好的解决方案是使用步长为 -1 的切片:object[::-1]

从速度角度来看,最好使用上述内置函数来反转列表。对于反转,与手动创建的循环或生成器相比,它们在短列表(10 个项目)上的速度要快 2 到 8 倍,在长列表上的速度要快 300 多倍。这是有道理的 - 它们是用母语(即 C)编写的,有专家创建、审查和优化。它们也不太容易出现缺陷,更有可能处理边缘和极端情况。

测试脚本

将此答案中的所有代码片段放在一起,制作一个脚本,该脚本将运行下面描述的不同列表反转方法。它将在运行 100,000 次时对每种方法进行计时。最后一节显示了长度为 2、10 和 1000 个项目的列表的结果。

from timeit import timeit
from copy import copy

def time_str_ms(t):
    return '{0:8.2f} ms'.format(t * 1000)

方法 1:使用 obj.reverse() 进行原地反转

如果目标只是反转现有列表中项目的顺序,而不循环遍历它们或获取要使用的副本,请使用该<list>.reverse()函数。直接在列表对象上运行此函数,所有项目的顺序将被反转:

请注意,以下将反转给定的原始变量,即使它也会返回反转的列表。即,您可以使用此函数输出创建副本。通常,您不会为此创建函数,但计时脚本需要它。

我们测试了这两种方法的性能 - 首先只是就地反转列表(更改原始列表),然后复制列表并随后反转它,以查看与其他方法相比这是否是创建反转副本的最快方法。

def rev_in_place(mylist):
    mylist.reverse()
    return mylist

def rev_copy_reverse(mylist):
    a = copy(mylist)
    a.reverse()
    return a

方法 2:使用切片反转列表obj[::-1]

内置索引切片方法允许您复制任何索引对象的一部分。

  • 不影响原物体

  • 它构建一个完整列表,而不是迭代器

通用语法是:<object>[first_index:last_index:step]。要利用切片创建一个简单的反向列表,请使用:<list>[::-1]。当将选项留空时,它会将它们设置为对象的第一个和最后一个元素的默认值(如果步长为负,则反转)。

索引允许使用负数,即从对象索引末尾向后计数(即 -2 是倒数第二个项目)。当步长为负数时,它将从最后一个项目开始,并按该数量向后索引。

def rev_slice(mylist):
    a = mylist[::-1]
    return a

reversed(obj)方法 3:使用迭代器函数反转列表

有一个reversed(indexed_object)函数:

  • 这将创建一个反向索引迭代器,而不是列表。如果您将其输入到循环中以获得更好的大型列表性能,那么这是一个很好的选择。

  • 这将创建一个副本,并且不会影响原始对象

使用原始迭代器进行测试,并从迭代器创建列表。

def reversed_iterator(mylist):
    a = reversed(mylist)
    return a

def reversed_with_list(mylist):
    a = list(reversed(mylist))
    return a

方法 4:使用自定义/手动索引的反向列表

正如时间所显示的,创建自己的索引方法不是一个好主意。除非您确实需要执行自定义操作,否则请使用内置方法。这仅仅意味着学习内置方法。

也就是说,列表规模较小时不会产生很大的损失,但当列表规模扩大时,损失就会变得非常大。我确信下面的代码可以进行优化,但它永远无法与内置方法相媲美,因为它们是直接用本机语言实现的。

def rev_manual_pos_gen(mylist):
    max_index = len(mylist) - 1
    return [ mylist[max_index - index] for index in range(len(mylist)) ]

def rev_manual_neg_gen(mylist):
    ## index is 0 to 9, but we need -1 to -10
    return [ mylist[-index-1] for index in range(len(mylist)) ]

def rev_manual_index_loop(mylist):
    a = []
    reverse_index = len(mylist) - 1
    for index in range(len(mylist)):
        a.append(mylist[reverse_index - index])
    return a
    
def rev_manual_loop(mylist):
    a = []
    reverse_index = len(mylist)
    for index, _ in enumerate(mylist):
        reverse_index -= 1
        a.append(mylist[reverse_index])
    return a

每种方法的计时

以下是脚本的其余部分,用于对每种反转方法进行计时。它表明使用迭代器进行反转obj.reverse()和创建reversed(obj)迭代器始终是最快的,而使用切片是创建副本的最快方法。

这也证明,除非万不得已,否则不要试图独自创造一种方法来做这件事!

loops_to_test = 100000
number_of_items = 10
list_to_reverse = list(range(number_of_items))
if number_of_items < 15:
    print("a: {}".format(list_to_reverse))
print('Loops: {:,}'.format(loops_to_test))
# List of the functions we want to test with the timer, in print order
fcns = [rev_in_place, reversed_iterator, rev_slice, rev_copy_reverse,
        reversed_with_list, rev_manual_pos_gen, rev_manual_neg_gen,
        rev_manual_index_loop, rev_manual_loop]
max_name_string = max([ len(fcn.__name__) for fcn in fcns ])
for fcn in fcns:
    a = copy(list_to_reverse) # copy to start fresh each loop
    out_str = ' | out = {}'.format(fcn(a)) if number_of_items < 15 else ''
    # Time in ms for the given # of loops on this fcn
    time_str = time_str_ms(timeit(lambda: fcn(a), number=loops_to_test))
    # Get the output string for this function
    fcn_str = '{}(a):'.format(fcn.__name__)
    # Add the correct string length to accommodate the maximum fcn name
    format_str = '{{fx:{}s}} {{time}}{{rev}}'.format(max_name_string + 4)
    print(format_str.format(fx=fcn_str, time=time_str, rev=out_str))

计时结果

注意:以下结果在 Python 3.7.9 上运行

结果表明,对于特定类型的逆向分析而言,内置方法的扩展效果最好。换句话说,随着对象元素数量的增加,内置方法比其他方法的表现更好。

直接实现您所需的内置方法比将事物串在一起效果更好。例如,如果您需要反向列表的副本,则切片是最好的选择 - 它比从list(reversed(obj))函数创建重复列表更快,也比复制列表然后执行就地操作更快obj.reverse(),但速度永远不会超过两倍。同时 - 对于大型列表,自定义方法可能需要更长的时间。

对于扩展,对于 1000 个项目列表,reversed(<list>)函数调用需要 ~30 毫秒来设置迭代器,就地反转仅需 ~55 毫秒,使用切片方法需要 ~210 毫秒来创建完整反转列表的副本,但我制作的最快的手动方法需要~8400 毫秒

列表中有 2 个项目:

a: [0, 1]
Loops: 100,000
rev_in_place(a):             24.70 ms | out = [1, 0]
reversed_iterator(a):        30.48 ms | out = <list_reverseiterator object at 0x0000020242580408>
rev_slice(a):                31.65 ms | out = [1, 0]
rev_copy_reverse(a):         63.42 ms | out = [1, 0]
reversed_with_list(a):       48.65 ms | out = [1, 0]
rev_manual_pos_gen(a):       98.94 ms | out = [1, 0]
rev_manual_neg_gen(a):       88.11 ms | out = [1, 0]
rev_manual_index_loop(a):    87.23 ms | out = [1, 0]
rev_manual_loop(a):          79.24 ms | out = [1, 0]

列表中有 10 个项目:

rev_in_place(a):             23.39 ms | out = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
reversed_iterator(a):        30.23 ms | out = <list_reverseiterator object at 0x00000290A3CB0388>
rev_slice(a):                36.01 ms | out = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
rev_copy_reverse(a):         64.67 ms | out = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
reversed_with_list(a):       50.77 ms | out = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
rev_manual_pos_gen(a):      162.83 ms | out = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
rev_manual_neg_gen(a):      167.43 ms | out = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
rev_manual_index_loop(a):   152.04 ms | out = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
rev_manual_loop(a):         183.01 ms | out = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

列表中有 1000 个项目:

rev_in_place(a):             56.37 ms
reversed_iterator(a):        30.47 ms
rev_slice(a):               211.42 ms
rev_copy_reverse(a):        295.74 ms
reversed_with_list(a):      418.45 ms
rev_manual_pos_gen(a):     8410.01 ms
rev_manual_neg_gen(a):    11054.84 ms
rev_manual_index_loop(a): 10543.11 ms
rev_manual_loop(a):       15472.66 ms

注意:2023 年 12 月将推出 Python 3.11 性能改进

我快速比较了 Python 3.7 和 3.11(刚才在同一台 PC 上),运行了 100 万次循环,看看 3.11 的性能改进会带来什么不同。Python 3.11 平均快 26.5%,手动和内置反转列表方法的速度大致相同。

解决方案 5:

我发现(与其他一些建议相反)这l.reverse()是迄今为止在 Python 3 和 2 中反转长列表的最快方法。我有兴趣知道其他人是否可以复制这些时间。

l[::-1]可能更慢,因为它在反转列表之前会先复制列表。list()在迭代器周围添加调用reversed(l)必定会增加一些开销。当然,如果您想要列表或迭代器的副本,请使用相应的方法,但如果您只想反转列表,那么这l.reverse()似乎是最快的方法。

功能

def rev_list1(l):
    return l[::-1]

def rev_list2(l):
    return list(reversed(l))

def rev_list3(l):
    l.reverse()
    return l

列表

l = list(range(1000000))

Python 3.5 计时

timeit(lambda: rev_list1(l), number=1000)
# 6.48
timeit(lambda: rev_list2(l), number=1000)
# 7.13
timeit(lambda: rev_list3(l), number=1000)
# 0.44

Python 2.7 计时

timeit(lambda: rev_list1(l), number=1000)
# 6.76
timeit(lambda: rev_list2(l), number=1000)
# 9.18
timeit(lambda: rev_list3(l), number=1000)
# 0.46

解决方案 6:

for x in array[::-1]:
    do stuff

解决方案 7:

array=[0,10,20,40]
for e in reversed(array):
  print e

解决方案 8:

另一个解决方案是使用numpy.flip

import numpy as np
array = [0, 10, 20, 40]
list(np.flip(array))
[40, 20, 10, 0]

解决方案 9:

您还可以使用数组索引的按位补码反向遍历数组:

>>> array = [0, 10, 20, 40]
>>> [array[~i] for i, _ in enumerate(array)]
[40, 20, 10, 0]

无论你做什么,都不要这样做;)

解决方案 10:

如果您想将反向列表的元素存储在其他变量中,那么您可以使用revArray = array[::-1]revArray = list(reversed(array))

但第一个变体稍微快一些:

z = range(1000000)
startTimeTic = time.time()
y = z[::-1]
print("Time: %s s" % (time.time() - startTimeTic))

f = range(1000000)
startTimeTic = time.time()
g = list(reversed(f))
print("Time: %s s" % (time.time() - startTimeTic))

输出:

Time: 0.00489711761475 s
Time: 0.00609302520752 s

解决方案 11:

使用列表理解:

[array[n] for n in range(len(array)-1, -1, -1)]

解决方案 12:

def reverse(my_list):
  L = len(my_list)
  for i in range(L/2):
    my_list[i], my_list[L-i - 1] = my_list[L-i-1], my_list[i]
  return my_list

解决方案 13:

您始终可以将列表视为堆栈,只需从列表的后端弹出堆栈顶部的元素即可。这样,您就可以利用堆栈的先进后出特性。当然,您正在使用第一个数组。我确实喜欢这种方法,因为它非常直观,因为您会看到一个列表从后端被使用,而另一个列表从前端构建。

>>> l = [1,2,3,4,5,6]; nl=[]
>>> while l:
        nl.append(l.pop())  
>>> print nl
[6, 5, 4, 3, 2, 1]

解决方案 14:

您的要求最直接的翻译成 Python 是这样的for语句:

for i in xrange(len(array) - 1, -1, -1):
   print i, array[i]

这虽然有些神秘,但也许有用。

解决方案 15:

通过切换相反索引的引用进行就地反转:

>>> l = [1,2,3,4,5,6,7]    
>>> for i in range(len(l)//2):
...     l[i], l[-1-i] = l[-1-i], l[i]
...
>>> l
[7, 6, 5, 4, 3, 2, 1]

解决方案 16:

>>> L = [1, 2, 3, 4]
>>> L = [L[-i] for i in range(1, len(L) + 1)]
>>> L
[4, 3, 2, 1]

解决方案 17:

>>> l = [1, 2, 3, 4, 5]
>>> print(reduce(lambda acc, x: [x] + acc, l, []))
[5, 4, 3, 2, 1]

解决方案 18:

此类使用 Python 魔术方法和迭代器进行反转,并反转列表:

class Reverse(object):
    """ Builds a reverse method using magic methods """

    def __init__(self, data):
        self.data = data
        self.index = len(data)


    def __iter__(self):
        return self

    def __next__(self):
        if self.index == 0:
            raise StopIteration

        self.index = self.index - 1
        return self.data[self.index]


REV_INSTANCE = Reverse([0, 10, 20, 40])

iter(REV_INSTANCE)

rev_list = []
for i in REV_INSTANCE:
    rev_list.append(i)

print(rev_list)  

输出

[40, 20, 10, 0]
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1579  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1355  
  信创产品在政府采购中的占比分析随着信息技术的飞速发展以及国家对信息安全重视程度的不断提高,信创产业应运而生并迅速崛起。信创,即信息技术应用创新,旨在实现信息技术领域的自主可控,减少对国外技术的依赖,保障国家信息安全。政府采购作为推动信创产业发展的重要力量,其对信创产品的采购占比情况备受关注。这不仅关系到信创产业的发展前...
信创和国产化的区别   8  
  信创,即信息技术应用创新产业,旨在实现信息技术领域的自主可控,摆脱对国外技术的依赖。近年来,国货国用信创发展势头迅猛,在诸多领域取得了显著成果。这一发展趋势对科技创新产生了深远的推动作用,不仅提升了我国在信息技术领域的自主创新能力,还为经济社会的数字化转型提供了坚实支撑。信创推动核心技术突破信创产业的发展促使企业和科研...
信创工作   9  
  信创技术,即信息技术应用创新产业,旨在实现信息技术领域的自主可控与安全可靠。近年来,信创技术发展迅猛,对中小企业产生了深远的影响,带来了诸多不可忽视的价值。在数字化转型的浪潮中,中小企业面临着激烈的市场竞争和复杂多变的环境,信创技术的出现为它们提供了新的发展机遇和支撑。信创技术对中小企业的影响技术架构变革信创技术促使中...
信创国产化   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用