如何在Python中反向迭代列表?
请参见:如何获得列表的反向副本(避免在.reverse后链接方法时使用单独的语句)?
如何在Python中反向迭代列表?
请参见:如何获得列表的反向副本(避免在.reverse后链接方法时使用单独的语句)?
当前回答
逆向方法综述
有三种不同的内置方法来反转列表。哪种方法最好取决于你是否需要:
就地反转现有列表(更改原始列表变量) 最好的解决方案是object.reverse()方法 创建反转列表的迭代器(因为要将其提供给for循环、生成器等)。 最好的解决方案是reversed(object),它创建了迭代器 创建列表的副本,只是顺序相反(以保留原始列表) 最佳解决方案是使用步长为-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:使用slices反转列表obj[::-1]
内置的索引切片方法允许您复制任何索引对象的一部分。
它不影响原来的对象 它构建一个完整的列表,而不是迭代器
通用语法是:<object>[first_index:last_index:step]。使用:<list>[::-1]来创建一个简单的反向列表。当一个选项为空时,它将它们设置为对象的第一个和最后一个元素的默认值(如果步长为负则相反)。
索引允许使用负数,它从对象索引的末尾向后计数(即-2是倒数第二项)。当步长为负数时,它将从最后一项开始,并向后索引该值。
def rev_slice(mylist):
a = mylist[::-1]
return a
方法3:使用reversed(obj)迭代器函数反转列表
有一个反向的(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))
计时结果
结果表明,使用最适合于特定类型反转的内置方法,缩放效果最好。换句话说,随着对象元素数量的增加,内置方法的速度甚至超过其他方法。
直接实现所需内容的内置方法比将内容串在一起要好。例如,如果你需要反向列表的副本,切片是最好的-它比从list(reversed(obj))函数创建一个副本列表快,也比创建一个列表副本然后在原地执行obj.reverse()快,但速度永远不会超过两倍。与此同时,自定义方法对于大型列表可能需要更长数量级的时间。
对于缩放,对于一个1000项的列表,reversed(<list>)函数调用需要大约30毫秒来设置迭代器,反向只需要大约55毫秒,使用slice方法需要大约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
其他回答
我在一次面试的python代码测试中遇到了这个问题。 以下是我的答案。 注意它适用于任何值任何长度
def get_reverse(list_check, count_num):
final_list =[]
for index in range(list_length):
value = list_check[count_num]
final_list.append(value)
count_num = count_num -1
return final_list
new_list = ['A', 'GOAT', 'C', 'D', 'Mac']
list_length = len(new_list)
x = list_length -1
print(get_reverse(new_list, x))
逆向方法综述
有三种不同的内置方法来反转列表。哪种方法最好取决于你是否需要:
就地反转现有列表(更改原始列表变量) 最好的解决方案是object.reverse()方法 创建反转列表的迭代器(因为要将其提供给for循环、生成器等)。 最好的解决方案是reversed(object),它创建了迭代器 创建列表的副本,只是顺序相反(以保留原始列表) 最佳解决方案是使用步长为-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:使用slices反转列表obj[::-1]
内置的索引切片方法允许您复制任何索引对象的一部分。
它不影响原来的对象 它构建一个完整的列表,而不是迭代器
通用语法是:<object>[first_index:last_index:step]。使用:<list>[::-1]来创建一个简单的反向列表。当一个选项为空时,它将它们设置为对象的第一个和最后一个元素的默认值(如果步长为负则相反)。
索引允许使用负数,它从对象索引的末尾向后计数(即-2是倒数第二项)。当步长为负数时,它将从最后一项开始,并向后索引该值。
def rev_slice(mylist):
a = mylist[::-1]
return a
方法3:使用reversed(obj)迭代器函数反转列表
有一个反向的(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))
计时结果
结果表明,使用最适合于特定类型反转的内置方法,缩放效果最好。换句话说,随着对象元素数量的增加,内置方法的速度甚至超过其他方法。
直接实现所需内容的内置方法比将内容串在一起要好。例如,如果你需要反向列表的副本,切片是最好的-它比从list(reversed(obj))函数创建一个副本列表快,也比创建一个列表副本然后在原地执行obj.reverse()快,但速度永远不会超过两倍。与此同时,自定义方法对于大型列表可能需要更长数量级的时间。
对于缩放,对于一个1000项的列表,reversed(<list>)函数调用需要大约30毫秒来设置迭代器,反向只需要大约55毫秒,使用slice方法需要大约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
使用反向(数组)可能是最好的方法。
>>> array = [1,2,3,4]
>>> for item in reversed(array):
>>> print item
如果你需要了解如何实现这个不使用内置反转。
def reverse(a):
midpoint = len(a)/2
for item in a[:midpoint]:
otherside = (len(a) - a.index(item)) - 1
temp = a[otherside]
a[otherside] = a[a.index(item)]
a[a.index(item)] = temp
return a
这需要O(N)时间。
>>> L = [1, 2, 3, 4]
>>> L = [L[-i] for i in range(1, len(L) + 1)]
>>> L
[4, 3, 2, 1]
你总是可以像对待堆栈一样对待列表,只是从列表的后端取出堆栈顶部的元素。这样你就可以利用堆栈的先进后出特性。当然你消耗的是第一个数组。我确实喜欢这种方法,因为它非常直观,因为您可以看到一个列表是从后端消耗的,而另一个是从前端构建的。
>>> l = [1,2,3,4,5,6]; nl=[]
>>> while l:
nl.append(l.pop())
>>> print nl
[6, 5, 4, 3, 2, 1]