), скопируйте список и добавьте элемент.

ьно часто я обнаружил необходимость обрабатывать список по парам. Мне было интересно, какой будет питон и эффективный способ сделать это, и нашел это в Google:

pairs = zip(t[::2], t[1::2])

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

import time
from itertools import islice, izip

def pairs_1(t):
    return zip(t[::2], t[1::2]) 

def pairs_2(t):
    return izip(t[::2], t[1::2]) 

def pairs_3(t):
    return izip(islice(t,None,None,2), islice(t,1,None,2))

A = range(10000)
B = xrange(len(A))

def pairs_4(t):
    # ignore value of t!
    t = B
    return izip(islice(t,None,None,2), islice(t,1,None,2))

for f in pairs_1, pairs_2, pairs_3, pairs_4:
    # time the pairing
    s = time.time()
    for i in range(1000):
        p = f(A)
    t1 = time.time() - s

    # time using the pairs
    s = time.time()
    for i in range(1000):
        p = f(A)
        for a, b in p:
            pass
    t2 = time.time() - s
    print t1, t2, t2-t1

Это были результаты на моем компьютере:

1.48668909073 2.63187503815 1.14518594742
0.105381965637 1.35109519958 1.24571323395
0.00257992744446 1.46182489395 1.45924496651
0.00251388549805 1.70076990128 1.69825601578

Если я правильно их интерпретирую, это должно означать, что реализация списков, индексация списков и нарезка списков в Python очень эффективны. Это результат утешительный и неожиданный.

Есть ли другой, «лучший» способ обхода списка в парах?

Обратите внимание, что если в списке нечетное количество элементов, то последний не будет ни в одной из пар.

Какой будет правильный способ обеспечить включение всех элементов?

Я добавил эти два предложения из ответов на тесты:

def pairwise(t):
    it = iter(t)
    return izip(it, it)

def chunkwise(t, size=2):
    it = iter(t)
    return izip(*[it]*size)

Вот результаты:

0.00159502029419 1.25745987892 1.25586485863
0.00222492218018 1.23795199394 1.23572707176
Результаты пока что

Наиболее питонны и очень эффективны:

pairs = izip(t[::2], t[1::2])

Самый эффективный и очень питонический:

pairs = izip(*[iter(t)]*2)

Мне потребовалось время, чтобы понять, что в первом ответе используются два итератора, а во втором - один.

Чтобы иметь дело с последовательностями с нечетным числом элементов, было предложено дополнить исходную последовательность добавлением одного элемента (None) в сочетании с предыдущим последним элементом, чего можно достичь с помощьюitertools.izip_longest().

в заключение

Обратите внимание, что в Python 3.xzip() ведет себя какitertools.izip(), а такжеitertools.izip() ушел

 Apalala07 янв. 2011 г., 19:51
@ Андрей Яффе Я дал критерий «лучший» в этом случае: эффективный и питонический.
 Andrew Jaffe07 янв. 2011 г., 20:18
@Apalala: я имею в виду, чтоисход наличие нечетного числа зависит от использования. Например: вы можете просто пропустить последний элемент, или добавить определенный известный фиктивный элемент, или дублировать последний
 Andrew Jaffe07 янв. 2011 г., 18:45
RE: «правильный путь» - нет «правильного» пути! Это зависит от варианта использования.
 SilentGhost08 янв. 2011 г., 23:38
@Apalala: потому что вместо муми-юмбо вы используетеtimeit модуль.
 tokland10 янв. 2011 г., 14:16
n-продублировано: просто в быстром поиске:stackoverflow.com/questions/4501636, stackoverflow.com/questions/4170295, stackoverflow.com/questions/434287

Ответы на вопрос(6)

Я не могу сказать наверняка, но я сомневаюсь в этом: любой другой обход включал бы больше кода Python, который должен быть интерпретирован. Встроенные функции, такие как zip (), написаны на C, что намного быстрее.

Какой будет правильный способ обеспечить включение всех элементов?

Проверьте длину списка и если он нечетный (len(list) & 1 == 1), скопируйте список и добавьте элемент.

ниже. Это совсем не Pythonic, я написал просто для удовольствия. Это похоже на @ THC4kpairwise функция, но она используетiter а такжеlambda затворы. Не используетitertools модуль и не поддерживаетfillvalue, Я положил это здесь, потому что кто-то может найти это интересным

pairwise = lambda t: iter((lambda f: lambda: (f(), f()))(iter(t).next), None)

pairs = zip(t[::2], t[1::2]) является лучшим, потому что его легче читать (а в Python 3zip автоматически возвращает итератор вместо списка).

Чтобы убедиться, что все элементы включены, вы можете просто расширить список наNone.

Затем, если список содержит нечетное количество элементов, последняя пара будет(item, None).

>>> t = [1,2,3,4,5]
>>> t.append(None)
>>> zip(t[::2], t[1::2])
[(1, 2), (3, 4), (5, None)]
>>> t = [1,2,3,4,5,6]
>>> t.append(None)
>>> zip(t[::2], t[1::2])
[(1, 2), (3, 4), (5, 6)]
>>> my_list = [1,2,3,4,5,6,7,8,9,10]
>>> my_pairs = list()
>>> while(my_list):
...     a = my_list.pop(0); b = my_list.pop(0)
...     my_pairs.append((a,b))
... 
>>> print(my_pairs)
[(1, 2), (3, 4), (5, 6), (7, 8), (9, 10)]

я бы сказал,рецепты поставляются в исходных документах Python (некоторые из которых очень похожи на ответы, предоставленные @JochenRitzel), вероятно, ваш лучший выбор;)

def grouper(iterable, n, fillvalue=None):
    "Collect data into fixed-length chunks or blocks"
    # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx
    args = [iter(iterable)] * n
    return izip_longest(fillvalue=fillvalue, *args)
Решение Вопроса

Мой любимый способ сделать это:

from itertools import izip

def pairwise(t):
    it = iter(t)
    return izip(it,it)

# for "pairs" of any length
def chunkwise(t, size=2):
    it = iter(t)
    return izip(*[it]*size)

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

from itertools import izip_longest
def blockwise(t, size=2, fillvalue=None):
    it = iter(t)
    return izip_longest(*[it]*size, fillvalue=fillvalue)
 max25 июл. 2016 г., 01:26
для вашего второго решения, не хотите ли вы избежать создания списка, если вы идете после производительности?
 Channing Moore18 дек. 2014 г., 17:56
Мне нравится это решение: оно лениво, и оно очень эффективно использует состояние итераторов. Вы можете даже сделать его однострочным, хотя, возможно, за счет читабельности:izip(*[iter(t)]*size)
 Apalala07 янв. 2011 г., 19:40
В первой (попарной) функции, похоже, отсутствует клонирование и продвижение второго итератора. Увидетьitertools раздел рецептов.
 Jochen Ritzel07 янв. 2011 г., 19:55
@Apalala: zip продвигает один и тот же итератор дважды.
 Apalala07 янв. 2011 г., 20:05
Конечно, вы правы, и попарно пока самый эффективный, я не знаю почему.

Ваш ответ на вопрос