Перемещение по списку в обратном порядке в Python

Так что я могу начать с len(collection) и закончить collection[0].

Я также хочу иметь доступ к индексу цикла.


person Joan Venge    schedule 09.02.2009    source источник


Ответы (24)


Используйте встроенную функцию reversed():

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

Чтобы также получить доступ к исходному индексу, используйте enumerate() в своем списке, прежде чем передавать его reversed() :

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

Поскольку enumerate() возвращает генератор, а генераторы не могут быть отменены, вам нужно сначала преобразовать его в list.

person Greg Hewgill    schedule 09.02.2009
comment
Спасибо. Из любопытства, этот обратный метод создает отдельную копию коллекции или просто изменяет цикл? - person Joan Venge; 09.02.2009
comment
Копия не создается, элементы меняются на лету при перемещении! Это важная особенность всех этих итерационных функций (все они заканчиваются на «ed»). - person Konrad Rudolph; 09.02.2009
comment
Функция reversed () делает копию коллекции. Чтобы изменить существующий список, используйте метод .reverse (). - person Greg Hewgill; 09.02.2009
comment
@Greg Hewgill Нет, это итератор по оригиналу, копия не создается! - person André; 09.02.2009
comment
@ Андре: ты, конечно, прав, но я использовал те же слова, что и вопрос, заданный Джоан, чтобы ответ было легче понять. Концептуально он создает копию. В реализации используется итератор. - person Greg Hewgill; 09.02.2009
comment
@Greg: ваш новый фрагмент кода для доступа к исходному индексу создает вторую копию исходной коллекции. Лучше было бы просто сделать что-то вроде i = len (a) - 1 - ridx - person Theran; 09.02.2009
comment
Отстой, что это должно пройти весь список перед итерацией ... Все еще проголосовало. - person Triptych; 09.02.2009
comment
Во избежание путаницы: reversed() не изменяет список. reversed() не копирует список (иначе потребовалось бы O (N) дополнительной памяти). Если вам нужно изменить список, используйте alist.reverse(); если вам нужна копия списка в обратном порядке, используйте alist[::-1]. - person jfs; 09.02.2009
comment
в этом ответе list (enumerate (a)) ДЕЙСТВИТЕЛЬНО создает копию. - person Triptych; 09.02.2009
comment
Он не совсем копирует, похоже, он выполняет ту же логику, что и ручной ход назад. Если вы вызываете reverse, затем изменяете список, а затем перебираете обратный список, изменения будут отражены функцией reversed (). - person Richard Levasseur; 09.02.2009
comment
Он абсолютно копирует список. Точнее, почтовый индекс списка с его позиционными индексами. - person Triptych; 09.02.2009
comment
@Triptych: reversed([a,b,c]) не создает копию [a, b, c]. - person jfs; 09.02.2009
comment
Я думаю, здесь есть некоторая путаница в том, какой именно пример мы обсуждаем. В первом примере используется только reversed (); во втором примере используется обратный (list (enumerate ())). Очевидно, что второй пример делает больше копий, чем первый. Я думаю, что в каком-то смысле вы все правы. :) - person Greg Hewgill; 09.02.2009
comment
@ JF, reversed () не делает копию, но list (enumerate ()) ДЕЙСТВИТЕЛЬНО делает копию. - person Triptych; 09.02.2009
comment
Спасибо за вводные данные, но похоже, что есть путаница в том, как реализованы эти методы. Под копией я имел в виду глубокую копию, а не итератор. Итераторы хороши, но глубокие копии могут значительно замедлить мой код для нескольких миллионов данных. - person Joan Venge; 09.02.2009
comment
@Joan, не стоит беспокоиться о сравнении «глубоких» и «мелких» копий. Это имеет значение только тогда, когда вы имеете дело с вложенными списками. Даже «неглубокая» копия будет стоить дорого - она ​​дублирует весь список. Я считаю, что мой ответ - единственный, который соответствует вашим спецификациям и не требует дополнительной памяти. - person Triptych; 10.02.2009
comment
Спасибо, Триптих, я имею в виду не коллекционную копию, а копию итератора. - person Joan Venge; 10.02.2009
comment
Хотя reversed(list(enumerate(a))) работает, но не оптимизирован. Здесь предлагается альтернатива с использованием лямбда-функции: christophe-simonis-at-tiny.blogspot.ca/2008/08/ - person Bill; 25.01.2016
comment
@ Билл Я думаю, enumerate(reversed(a)) тоже могло бы стать решением. Однако индексы будут в обратном порядке, т.е. последний элемент имеет индекс 0. - person Snow bunting; 06.07.2017
comment
@Snowbunting, индексы можно легко перевернуть с помощью len(a)-i-1, а при доступе к списку b такой же длины b[-i-1] эквивалентно b[len(a)-i-1] - person CervEd; 03.06.2019
comment
Фактически, вы можете использовать следующий генератор (-(ri+1), val) for ri, val in enumerate(reversed(foo)), см. Мой ответ например - person CervEd; 03.06.2019
comment
На самом деле я бы предпочел версию @disooqi / Francsics (for i in range(len(a)-1, -1, -1)), поскольку это одна строка и действительно позволяет избежать копирования или создания списка. - person Markus; 04.06.2019

Ты можешь сделать:

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

(Или все, что вы хотите сделать в цикле for.)

Срез [::-1] переворачивает список в цикле for (но фактически не изменяет ваш список «навсегда»).

person mipadi    schedule 09.02.2009
comment
[::-1] создает неглубокую копию, поэтому он не меняет массив ни навсегда, ни временно. - person jfs; 09.02.2009
comment
Это немного медленнее, чем при использовании реверса, по крайней мере, в Python 2.7 (проверено). - person kgriffs; 02.01.2014
comment
Как работает этот ответ: он создает нарезанную копию списка с параметрами: начальная точка: не указана (становится длиной списка, поэтому начинается с конца), end point: unspecified (становится некоторым магическим числом, отличным от 0, возможно -1, поэтому заканчивается в начале) и step: -1 (выполняет итерацию назад по списку, 1 элемента за раз). - person Edward; 16.05.2016
comment
Я тоже тестировал это (python 2.7), и он был на ~ 10% медленнее при использовании [:: - 1] по сравнению с reversed() - person RustyShackleford; 26.07.2017

Сделать это можно так:

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

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

Итак, ваша догадка была довольно близкой :) Немного неудобно, но в основном она гласит: начните с 1 меньше, чем len(collection), продолжайте, пока не дойдете до значения непосредственно перед -1, с шагом -1.

К вашему сведению, функция help очень полезна, поскольку позволяет просматривать документы для чего-либо из консоли Python, например:

help(range)

person Alan Rowarth    schedule 09.02.2009
comment
Для версий Python до 3.0 я считаю, что xrange предпочтительнее range для больших len (collection). - person Brian M. Hunt; 10.02.2009
comment
Я считаю, что вы правы :) iirc, range () генерирует весь диапазон как массив, но xrange () возвращает итератор, который генерирует значения только по мере необходимости. - person Alan Rowarth; 10.02.2009
comment
Это выглядит слишком странно с таким количеством -1. Я бы просто сказал reversed(xrange(len(collection))) - person musiphil; 07.09.2013

Если вам нужен индекс цикла и вы не хотите дважды проходить весь список или использовать дополнительную память, я бы написал генератор.

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
person Triptych    schedule 09.02.2009
comment
Я бы назвал функцию enumerate_reversed, но это может быть только моим вкусом. Я считаю, что ваш ответ наиболее точен на конкретный вопрос. - person tzot; 09.02.2009
comment
reversed(xrange(len(L))) производит те же индексы, что и xrange(len(L)-1, -1, -1). - person jfs; 10.02.2009
comment
@JF - да, в то время я думал, что у меня есть причина не делать этого, но теперь я не могу вспомнить. Меняем это сейчас. - person Triptych; 10.02.2009
comment
Это, но как однострочный генератор: ((i, sequence [i]) для i в обратном порядке (xrange (len (sequence)))) - person lkraider; 17.08.2014
comment
Я предпочитаю меньше движущихся частей, чтобы понять: for index, item in enumerate(reversed(L)): print len(L)-1-index, item - person Don Kirkby; 06.11.2014
comment
@Triptych Мне просто пришлось справиться с тем фактом, что enumerate from reversed () не дает обратных индексов, и ваш код очень помог. Этот метод должен быть в стандартной библиотеке. - person oski86; 21.07.2015
comment
reversed () в этом случае не нужен. xrange(len(L)-1, -1, -1) должен выполнить свою работу. Кстати, почему reversed(xrange()) работает, а reversed(enumerate()) нет? - person Antoine Pinsard; 26.08.2015
comment
reversed (xrange ()) работает, потому что объект xrange имеет метод __reversed__, а также методы __len__ и __getitem__, и reversed может обнаружить это и использовать их. Но в объекте перечисления нет __reversed__, __len__ или __getitem__. Но почему не перечисляет их? Я этого не знаю. - person FutureNerd; 24.09.2015
comment
@FutureNerd, потому что enumerate - это чистый объект-итератор, в то время как xrange технически является последовательностью. iter(xrange(..)) однако имеет те же ограничения, что и enumerate и любой другой итератор. - person Tadhg McDonald-Jensen; 14.05.2016

Встроенная функция reversed удобна:

for item in reversed(sequence):

В документации для reverse объясняются его ограничения.

Для случаев, когда мне нужно пройти последовательность в обратном порядке вместе с индексом (например, для модификаций на месте, изменяющих длину последовательности), у меня есть эта функция, определяющая мой модуль codeutil:

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

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

Это позволяет избежать создания копии последовательности. Очевидно, что reversed ограничения все еще применяются.

person tzot    schedule 11.10.2011

Подход без импорта:

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

or

for i in arr[::-1]:
    print(i)
person Kenan    schedule 06.12.2016
comment
Этот ответ должен быть верхним, первый подход оставляет список нетронутым, копии не делаются, и мы просто перемещаем индексы в обратном направлении. Очень быстро. Второй подход создаст новый список, так что имейте в виду. - person Amro Younes; 09.04.2021

Кроме того, вы можете использовать функции «диапазона» или «подсчета». Следующее:

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

3 baz
2 bar
1 foo

Вы также можете использовать "count" из itertools следующим образом:

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
person disooqi    schedule 06.04.2018
comment
Код в вашем первом блоке не дает правильного вывода; вывод на самом деле 3 foo\n2 bar\n1 baz - person amiller27; 22.06.2018
comment
Чтобы избежать использования [i-1] в первом примере, используйте этот диапазон range (len (a) -1, -1, -1). Это более упрощенно. - person Francisc; 24.12.2018

Как насчет того, чтобы не создавать новый список, вы можете обойтись путем индексации:

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

OR

>>> length = len(foo)
>>> for i in range(length):
...     print foo[length-i-1]
...
4d
3c
2b
1a
>>>
person James    schedule 15.02.2014

Мне нравится однострочный генератор:

((i, sequence[i]) for i in reversed(xrange(len(sequence))))
person lkraider    schedule 17.08.2014

В python 3 список создает копию, поэтому reversed(list(enumerate(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)
person Barney Szabolcs    schedule 14.07.2018
comment
это заставляет меня плакать - person CervEd; 06.05.2021
comment
@CervEd Вы абсолютно правы, я тоже заплакал, когда вернулся. Обновил свой ответ. - хотя дальность заставляет меня еще немного поплакать ... - person Barney Szabolcs; 08.05.2021

Используйте list.reverse(), а затем повторите, как обычно.

http://docs.python.org/tutorial/datastructures.html

person Bill Konrad    schedule 09.02.2009
comment
Для больших списков это пустая трата времени ЦП. Вместо этого используйте reversed. - person Nelo Mitranim; 08.11.2020

во что бы то ни стало, вы тоже можете это сделать. очень простой.

a = [1, 2, 3, 4, 5, 6, 7]
for x in xrange(len(a)):
    x += 1
    print a[-x]
person emorphus    schedule 27.02.2017
comment
Вы также можете сделать print a[-(x+1)] и избежать переназначения индекса в теле цикла. - person Malcolm; 26.11.2017

здесь пригодится обратная функция:

myArray = [1,2,3,4]
myArray.reverse()
for x in myArray:
    print x
person bchhun    schedule 09.02.2009
comment
list.reverse () не имеет возвращаемого значения - person Georg Schölly; 09.02.2009

Если вам нужен индекс, а ваш список невелик, наиболее читаемый способ - сделать reversed(list(enumerate(your_list))), как сказано в принятом ответе. Но это создает копию вашего списка, поэтому, если ваш список занимает большую часть вашей памяти, вам придется вычесть индекс, возвращаемый enumerate(reversed()), из len()-1.

Если вам просто нужно сделать это один раз:

a = ['b', 'd', 'c', 'a']

for index, value in enumerate(reversed(a)):
    index = len(a)-1 - index

    do_something(index, value)

или если вам нужно сделать это несколько раз, вы должны использовать генератор:

def enumerate_reversed(lyst):
    for index, value in enumerate(reversed(lyst)):
        index = len(lyst)-1 - index
        yield index, value

for index, value in enumerate_reversed(a):
    do_something(index, value)
person Boris    schedule 06.09.2018

Я думаю, что самый элегантный способ - преобразовать enumerate и reversed с помощью следующего генератора

(-(ri+1), val) for ri, val in enumerate(reversed(foo))

который генерирует обратный enumerate итератору

Пример:

foo = [1,2,3]
bar = [3,6,9]
[
    bar[i] - val
    for i, val in ((-(ri+1), val) for ri, val in enumerate(reversed(foo)))
]

Результат:

[6, 4, 2]
person CervEd    schedule 03.06.2019

Чтобы использовать отрицательные индексы: начните с -1 и отступайте на -1 на каждой итерации.

>>> a = ["foo", "bar", "baz"]
>>> for i in range(-1, -1*(len(a)+1), -1):
...     print i, a[i]
... 
-1 baz
-2 bar
-3 foo
person stroz    schedule 01.06.2016

Вы также можете использовать цикл while:

i = len(collection)-1
while i>=0:
    value = collection[i]
    index = i
    i-=1
person Yuval A.    schedule 27.07.2016

Вы можете использовать отрицательный индекс в обычном цикле for:

>>> collection = ["ham", "spam", "eggs", "baked beans"]
>>> for i in range(1, len(collection) + 1):
...     print(collection[-i])
... 
baked beans
eggs
spam
ham

Чтобы получить доступ к индексу, как если бы вы перебирали перевернутую копию коллекции, используйте i - 1:

>>> for i in range(1, len(collection) + 1):
...     print(i-1, collection[-i])
... 
0 baked beans
1 eggs
2 spam
3 ham

Чтобы получить доступ к исходному, необратимому индексу, используйте len(collection) - i:

>>> for i in range(1, len(collection) + 1):
...     print(len(collection)-i, collection[-i])
... 
3 baked beans
2 eggs
1 spam
0 ham
person Malcolm    schedule 26.11.2017

Предполагая, что задача состоит в том, чтобы найти последний элемент, который удовлетворяет некоторому условию в списке (т.е. первый при взгляде назад), я получаю следующие числа:

>>> min(timeit.repeat('for i in xrange(len(xs)-1,-1,-1):\n    if 128 == xs[i]: break', setup='xs, n = range(256), 0', repeat=8))
4.6937971115112305
>>> min(timeit.repeat('for i in reversed(xrange(0, len(xs))):\n    if 128 == xs[i]: break', setup='xs, n = range(256), 0', repeat=8))
4.809093952178955
>>> min(timeit.repeat('for i, x in enumerate(reversed(xs), 1):\n    if 128 == x: break', setup='xs, n = range(256), 0', repeat=8))
4.931743860244751
>>> min(timeit.repeat('for i, x in enumerate(xs[::-1]):\n    if 128 == x: break', setup='xs, n = range(256), 0', repeat=8))
5.548468112945557
>>> min(timeit.repeat('for i in xrange(len(xs), 0, -1):\n    if 128 == xs[i - 1]: break', setup='xs, n = range(256), 0', repeat=8))
6.286104917526245
>>> min(timeit.repeat('i = len(xs)\nwhile 0 < i:\n    i -= 1\n    if 128 == xs[i]: break', setup='xs, n = range(256), 0', repeat=8))
8.384078979492188

Итак, самый уродливый вариант xrange(len(xs)-1,-1,-1) - самый быстрый.

person wonder.mice    schedule 10.09.2018
comment
Какая это версия Python? Поскольку диапазон по-прежнему выполняет работу xrange в версии 3.6 - person mrKindo; 11.09.2020

Если вы не возражаете против отрицательного значения индекса, вы можете:

>>> a = ["foo", "bar", "baz"]
>>> for i in range(len(a)):
...     print(~i, a[~i]))
-1 baz
-2 bar
-3 foo
person jss367    schedule 21.02.2019

Другие ответы хороши, но если вы хотите использовать стиль понимания списка

collection = ['a','b','c']
[item for item in reversed( collection ) ]
person fedmich    schedule 27.07.2013
comment
Разве это не то же самое, что перевернутый (сбор)? Добавление понимания списка ничего не делает, кроме ненужных вычислений. Это похоже на запись a = [item for item in [1, 2, 3]] vs a = [1, 2, 3]. - person EpicDavi; 21.06.2017

Простой способ:

n = int(input())
arr = list(map(int, input().split()))

for i in reversed(range(0, n)):
    print("%d %d" %(i, arr[i]))
person rashedcs    schedule 26.12.2017

input_list = ['foo','bar','baz']
for i in range(-1,-len(input_list)-1,-1)
    print(input_list[i])

Я думаю, что это также простой способ сделать это ... читать с конца и продолжать уменьшать до длины списка, так как мы никогда не выполняем "конечный" индекс, поэтому добавляем -1 также

person Varun Maurya    schedule 22.07.2018

вы можете использовать генератор:

li = [1,2,3,4,5,6]
len_li = len(li)
gen = (len_li-1-i for i in range(len_li))

наконец:

for i in gen:
    print(li[i])

надеюсь, это поможет вам.

person Xin    schedule 14.11.2019