Итерация по парам в списке (круговая мода) в Python - PullRequest
35 голосов
/ 11 августа 2009

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

Я думал о двух нелепых способах сделать это:

def pairs(lst):
    n = len(lst)
    for i in range(n):
        yield lst[i],lst[(i+1)%n]

и

def pairs(lst):
    return zip(lst,lst[1:]+[lst[:1]])

ожидаемый результат:

>>> for i in pairs(range(10)):
    print i

(0, 1)
(1, 2)
(2, 3)
(3, 4)
(4, 5)
(5, 6)
(6, 7)
(7, 8)
(8, 9)
(9, 0)
>>> 

какие-нибудь предложения о более питоническом способе сделать это? может быть, есть предопределенная функция, о которой я не слышал?

также может быть интересна более общая n-кратная (с парами, тройками и т. Д. Вместо пар) версия.

Ответы [ 13 ]

27 голосов
/ 11 августа 2009
def pairs(lst):
    i = iter(lst)
    first = prev = item = i.next()
    for item in i:
        yield prev, item
        prev = item
    yield item, first

Работает с любой непустой последовательностью, индексация не требуется.

9 голосов
/ 11 августа 2009

Я закодировал себя как общие версии кортежей, мне нравится первая из-за ее элегантной простоты, чем больше я на нее смотрю, тем более питонской она мне кажется ... в конце концов, что более питонское, чем одно вкладыш с zip, расширение аргумента звездочкой, списки, списки, списки и «диапазон»?

def ntuples(lst, n):
    return zip(*[lst[i:]+lst[:i] for i in range(n)])

Версия itertools должна быть достаточно эффективной даже для больших списков ...

from itertools import *
def ntuples(lst, n):
    return izip(*[chain(islice(lst,i,None), islice(lst,None,i)) for i in range(n)])

И версия для неиндексируемых последовательностей:

from itertools import *
def ntuples(seq, n):
    iseq = iter(seq)
    curr = head = tuple(islice(iseq, n))
    for x in chain(iseq, head):
        yield curr
        curr = curr[1:] + (x,)

В любом случае, спасибо всем за ваши предложения! : -)

6 голосов
/ 27 марта 2012

Мне, как всегда, нравиться тройник:

from itertools import tee, izip, chain

def pairs(iterable):
    a, b = tee(iterable)
    return izip(a, chain(b, [next(b)]))
5 голосов
/ 11 августа 2009

Это может быть удовлетворительным:

def pairs(lst):
    for i in range(1, len(lst)):
        yield lst[i-1], lst[i]
    yield lst[-1], lst[0]

>>> a = list(range(5))
>>> for a1, a2 in pairs(a):
...     print a1, a2
...
0 1
1 2
2 3
3 4
4 0

Если вам нравятся подобные вещи, посмотрите статьи на Python на wordaligned.org . У автора особая любовь к генераторам в python.

2 голосов
/ 11 августа 2009

Я бы сделал это так (в основном потому, что я могу читать это):

class Pairs(object):
    def __init__(self, start):
        self.i = start
    def next(self):
        p, p1 = self.i, self.i + 1
        self.i = p1
        return p, p1
    def __iter__(self):
        return self

if __name__ == "__main__":
    x = Pairs(0)
    y = 1
    while y < 20:
        print x.next()
        y += 1

дает:

(0, 1)
(1, 2)
(2, 3)
(3, 4)
(4, 5)
(5, 6)
(6, 7)
(7, 8)
(8, 9)
1 голос
/ 05 июня 2011
[(i,(i+1)%len(range(10))) for i in range(10)]

заменить диапазон (10) нужным списком.

В общем, "круговая индексация" довольно проста в Python; просто используйте:

a[i%len(a)] 
0 голосов
/ 20 апреля 2014

Конечно, вы всегда можете использовать deque :

from collections import deque
from itertools import *

def pairs(lst, n=2):
    itlst = iter(lst)
    start = list(islice(itlst, 0, n-1))
    deq = deque(start, n)
    for elt in chain(itlst, start):
        deq.append(elt)
        yield list(deq)
0 голосов
/ 15 мая 2013
def pairs(ex_list):
    for i, v in enumerate(ex_list):
        if i < len(list) - 1:
            print v, ex_list[i+1]
        else:
            print v, ex_list[0]

Enumerate возвращает кортеж с номером индекса и значением. Я печатаю значение и следующий элемент списка ex_list[i+1]. If i < len(list) - 1 означает, что v - , а не - последний член списка Если это так: выведите v и первый элемент списка print v, ex_list[0].

Edit:

Вы можете вернуть список. Просто добавьте напечатанные кортежи в список и верните его.

def pairs(ex_list):
    result = []
    for i, v in enumerate(ex_list):
        if i < len(list) - 1:
            result.append((v, ex_list[i+1]))
        else:
            result.append((v, ex_list[0]))
    return result
0 голосов
/ 10 апреля 2011

Вот версия, которая поддерживает необязательный начальный индекс (например, для возврата (4, 0) в качестве первой пары, используйте start = -1:

import itertools

def iterrot(lst, start = 0):

    if start == 0:
        i = iter(lst)
    elif start > 0:
        i1 = itertools.islice(lst, start, None)
        i2 = itertools.islice(lst, None, start)
        i = itertools.chain(i1, i2)
    else:
        # islice doesn't support negative slice indices so...
        lenl = len(lst)
        i1 = itertools.islice(lst, lenl + start, None)
        i2 = itertools.islice(lst, None, lenl + start)
        i = itertools.chain(i1, i2)
    return i


def iterpairs(lst, start = 0):

    i = iterrot(lst, start)     

    first = prev = i.next()
    for item in i:
        yield prev, item
        prev = item
    yield prev, first


def itertrios(lst, start = 0):

    i = iterrot(lst, start)     

    first = prevprev = i.next()
    second = prev = i.next()
    for item in i:
        yield prevprev, prev, item
        prevprev, prev = prev, item

    yield prevprev, prev, first
    yield prev, first, second
0 голосов
/ 04 января 2011
i=(range(10))

for x in len(i):
    print i[:2]
    i=i[1:]+[i[1]]

более питон, чем это невозможно

...