Подтвердить что ты не робот

Круговой цикл Python для цикла

Есть ли хороший метод Pythonic для перебора списка, перенастройка пары элементов? Последний элемент должен быть сопряжен с первым.

Так, например, если у меня есть список [1, 2, 3], я бы хотел получить следующие пары:

  • 1 - 2
  • 2 - 3
  • 3 - 1
4b9b3361

Ответ 1

Pythonic способ доступа к списку попарно: zip(L, L[1:]). Чтобы связать последний элемент с первым:

>>> L = [1, 2, 3]
>>> zip(L, L[1:] + L[:1])
[(1, 2), (2, 3), (3, 1)]

Ответ 2

Я бы использовал deque с zip, чтобы достичь этого.

>>> from collections import deque
>>>
>>> l = [1,2,3]
>>> d = deque(l)
>>> d.rotate(-1)
>>> zip(l, d)
[(1, 2), (2, 3), (3, 1)]

Ответ 3

Я бы немного изменил рецепт pairwise в itertools документации:

def pairwise_circle(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ... (s<last>,s0)"
    a, b = itertools.tee(iterable)
    first_value = next(b, None)
    return itertools.zip_longest(a, b,fillvalue=first_value)

Это просто сохранит ссылку на первое значение и когда второй итератор будет исчерпан, zip_longest заполнит последнее место первым значением.

(Также обратите внимание, что он работает с итераторами, такими как генераторы, а также итерами, такими как списки/кортежи.)

Обратите внимание, что @Barry solution очень похоже на это, но немного легче понять, на мой взгляд, и проще выходить за рамки одного элемента.

Ответ 4

Я бы пару itertools.cycle с zip:

import itertools

def circular_pairwise(l):
    second = itertools.cycle(l)
    next(second)
    return zip(l, second)

cycle возвращает итерабельность, которая возвращает значения своего аргумента по порядку, переходя от последнего значения к первому.

Мы пропустим первое значение, поэтому оно начинается с позиции 1 (а не 0).

Затем мы zip с оригинальным, неавтоматизированным списком. zip хорош, потому что он останавливается, когда любой из его аргументов iterables исчерпан.

Выполнение этого способа позволяет избежать создания любых промежуточных списков: cycle содержит ссылку на оригинал, но не копирует его. zip работает таким же образом.

Важно отметить, что это будет нарушено, если вход является iterator, например file, (или map или zip в ), поскольку продвижение в одном месте (через next(second)) автоматически приведет итератор во все другие. Это легко решить с помощью itertools.tee, который создает два независимо работающих итератора по исходному итерабельному:

def circular_pairwise(it):
    first, snd = itertools.tee(it)
    second = itertools.cycle(snd)
    next(second)
    return zip(first, second)

tee может использовать большие объемы дополнительного хранилища, например, если один из возвращенных итераторов используется до того, как другой коснется, но поскольку мы только когда-либо имеем разницу в один шаг, дополнительное хранилище минимально.

Ответ 5

Есть более эффективные способы (которые не создают временные списки), но я думаю, что это наиболее краткий:

> l = [1,2,3]
> zip(l, (l+l)[1:])
[(1, 2), (2, 3), (3, 1)]

Ответ 6

Я бы использовал понимание списка и воспользовался тем, что l[-1] - последний элемент.

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

Вам не нужен временный список.

Ответ 7

Pairwise круговой Python 'for' loop

Если вам нравится принятый ответ,

zip(L, L[1:] + L[:1])

вы можете намного увеличить объем памяти с семантически одним и тем же кодом, используя itertools:

from itertools import islice, chain #, izip as zip # uncomment if Python 2

И это едва материализует что-либо в памяти за пределами исходного списка (при условии, что список относительно велик):

zip(l, chain(islice(l, 1, None), islice(l, None, 1)))

Чтобы использовать, просто используйте (например, со списком):

>>> list(zip(l, chain(islice(l, 1, None), islice(l, None, 1))))
[(1, 2), (2, 3), (3, 1)]

Это можно сделать доступным для любой ширины:

def cyclical_window(l, width=2):
    return zip(*[chain(islice(l, i, None), islice(l, None, i)) for i in range(width)])

и использование:

>>> l = [1, 2, 3, 4, 5]
>>> cyclical_window(l)
<itertools.izip object at 0x112E7D28>
>>> list(cyclical_window(l))
[(1, 2), (2, 3), (3, 4), (4, 5), (5, 1)]
>>> list(cyclical_window(l, 4))
[(1, 2, 3, 4), (2, 3, 4, 5), (3, 4, 5, 1), (4, 5, 1, 2), (5, 1, 2, 3)]

Неограниченное поколение с itertools.tee с cycle

Вы также можете использовать tee, чтобы избежать создания объекта с избыточным циклом:

from itertools import cycle, tee
ic1, ic2 = tee(cycle(l))
next(ic2)    # must still queue up the next item

и теперь:

>>> [(next(ic1), next(ic2)) for _ in range(10)]
[(1, 2), (2, 3), (3, 1), (1, 2), (2, 3), (3, 1), (1, 2), (2, 3), (3, 1), (1, 2)]

Это невероятно эффективно, ожидаемое использование iter с next и элегантное использование cycle, tee и zip.

Не передавайте cycle непосредственно на list, если вы не сохранили свою работу и не успели зайти в компьютер, когда вы исчерпали свою память - если вам повезет, через некоторое время ваша ОС будет убивать процесс, прежде чем он выйдет из строя на вашем компьютере.

Встроенные функции Pure Python

Наконец, никаких стандартных импортов lib, но это работает только до длины исходного списка (IndexError в противном случае.)

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

Вы можете продолжить это с помощью modulo:

>>> len_l = len(l)
>>> [(l[i % len_l], l[(i + 1) % len_l]) for i in range(10)]
[(1, 2), (2, 3), (3, 1), (1, 2), (2, 3), (3, 1), (1, 2), (2, 3), (3, 1), (1, 2)]

Ответ 8

Удивительно, как много разных способов решить эту проблему.

Вот еще один. Вы можете использовать рецепт pairwise, но вместо zipping с помощью b, chain это первый элемент, который вы уже выскочили. Не нужно cycle, когда нам нужно только одно дополнительное значение:

from itertools import chain, izip, tee

def pairwise_circle(iterable):
    a, b = tee(iterable)
    first = next(b, None)
    return izip(a, chain(b, (first,)))

Ответ 9

Мне нравится решение, которое не изменяет исходный список и не копирует список во временное хранилище:

def circular(a_list):
    for index in range(len(a_list) - 1):
        yield a_list[index], a_list[index + 1]
    yield a_list[-1], a_list[0]

for x in circular([1, 2, 3]):
    print x

Вывод:

(1, 2)
(2, 3)
(3, 1)

Я могу представить, что это используется для некоторых очень больших данных в памяти.

Ответ 10

Этот будет работать, даже если список l потребляет большую часть системной памяти. (Если что-то гарантирует, что этот случай будет невозможным, тогда zip, как опубликовано chepner, прекрасно)

l.append( l[0] )
for i in range( len(l)-1):
   pair = l[i],l[i+1]
   # stuff involving pair
del l[-1] 

или более обобщенно (работает для любого смещения n i.e. l[ (i+n)%len(l) ])

for i in range( len(l)):
   pair = l[i], l[ (i+1)%len(l) ]
   # stuff

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

Похоже, что часто считается, что индексирование списка с целым индексом является непитоновым и лучше всего избегать. Зачем?

Ответ 11

Это мое решение, и для меня это выглядит достаточно Pythonic:

l = [1,2,3]

for n,v in enumerate(l):
    try:
        print(v,l[n+1])
    except IndexError:
        print(v,l[0])

печатает:

1 2
2 3
3 1

Версия функции генератора:

def f(iterable):
    for n,v in enumerate(iterable):
        try:
            yield(v,iterable[n+1])
        except IndexError:
            yield(v,iterable[0])

>>> list(f([1,2,3]))
[(1, 2), (2, 3), (3, 1)]

Ответ 12

Как насчет этого?

li = li+[li[0]]
pairwise = [(li[i],li[i+1]) for i in range(len(li)-1)]

Ответ 13

from itertools import izip, chain, islice

itr = izip(l, chain(islice(l, 1, None), islice(l, 1)))

(Как указано выше с @j-f-sebastian "zip" answer, но используя itertools.)

NB: EDITED, учитывая полезный подталкивание от @200_success. ранее был:

itr = izip(l, chain(l[1:], l[:1]))

Ответ 14

Еще одна попытка

>>> L = [1,2,3]
>>> zip(L,L[1:]) + [(L[-1],L[0])]
[(1, 2), (2, 3), (3, 1)]

Ответ 15

Если вы не хотите потреблять слишком много памяти, вы можете попробовать свое решение:

[(l[i], l[(i+1) % len(l)]) for i, v in enumerate(l)]

Это немного медленнее, но потребляет меньше памяти.

Ответ 16

L = [1, 2, 3] a = zip (L, L [1:] + L [: 1]) для я в:   b = список (i)   print b

Ответ 17

похоже, что комбинации выполняли бы эту работу.

from itertools import combinations
x=combinations([1,2,3],2)

это даст генератор. это можно затем повторить как таковое

for i in x:
  print i

результаты выглядят примерно так:

(1, 2)
(1, 3)
(2, 3)