17 ответов:
типичный для Python способ, чтобы открыть список попарно-это:
zip(L, L[1:]). Чтобы подключить последний элемент к первому:>>> L = [1, 2, 3] >>> zip(L, L[1:] + L[:1]) [(1, 2), (2, 3), (3, 1)]
Я бы использовал небольшую модификацию
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заполнить последнее место с первым значением.(также обратите внимание, что он работает с итераторами, такими как генераторы, а также с итерациями, такими как списки/кортежи.)
отметим, что @Барри очень похоже на это, но a немного легче понять на мой взгляд и легче выйти за рамки одного элемента.
я бы пару
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на python-3), как продвижение в одном месте (черезnext(second)) будет автоматически продвигать итератор во всех остальных. Это легко решается с помощьюitertools.tee, который производит два независимо работающих итератора над исходным iterable:def circular_pairwise(it): first, snd = itertools.tee(it) second = itertools.cycle(snd) next(second) return zip(first, second)
teeможете используйте большие объемы дополнительного хранилища, например, если один из возвращенных итераторов используется до того, как другой будет затронут, но поскольку у нас есть только одна разница в шагах, дополнительное хранилище минимально.
есть более эффективные способы (которые не строили временные списки), но я думаю, что это самый лаконичный:
> l = [1,2,3] > zip(l, (l+l)[1:]) [(1, 2), (2, 3), (3, 1)]
Я бы использовал понимание списка и воспользовался тем, что
l[-1]последний элемент.>>> l = [1,2,3] >>> [(l[i-1],l[i]) for i in range(len(l))] [(3, 1), (1, 2), (2, 3)]вам не нужен временный список таким образом.
попарно круговой 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)]вы можете продолжить это по модулю:
>>> 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)]
удивительно, как много разных способов решить эту проблему.
вот еще одна. Вы можете использовать
pairwiseрецепт, но вместо молнии с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,)))
мне нравится решение, которое не изменяет исходный список и не копирует список во временное хранилище:
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)Я могу себе представить, что это используется на некоторых очень больших данных в памяти.
это будет работать, даже если список
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т. е.l[ (i+n)%len(l) ])for i in range( len(l)): pair = l[i], l[ (i+1)%len(l) ] # stuffпри условии, что вы находитесь в системе с прилично быстрым делением по модулю (т. е. не какой-то Гороховой встроенной системой).
там, кажется, часто придерживаются мнения, что индексация списка с целочисленным индексом ООН-подходящие для Python и лучше избегать. Зачем?
Это мое решение, и оно выглядит достаточно весть для меня:
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)]
from itertools import izip, chain, islice itr = izip(l, chain(islice(l, 1, None), islice(l, 1)))(Как указано выше с @J-f-ответ Себастьяна "zip", но с помощью itertools.)
NB:редактировать учитывая полезный толчок от @200_success. ранее было:
itr = izip(l, chain(l[1:], l[:1]))
Если вы не хотите, чтобы потреблять слишком много памяти, вы можете попробовать мое решение:
[(l[i], l[(i+1) % len(l)]) for i, v in enumerate(l)]Это немного медленнее, но потребляет меньше памяти.
похоже, что комбинации сделают эту работу.
from itertools import combinations x=combinations([1,2,3],2)это даст генератор. затем это можно повторить как таковой
for i in x: print iрезультаты будут выглядеть примерно так
(1, 2) (1, 3) (2, 3)
Comments