Pares da lista única

Muitas vezes, descobri a necessidade de processar uma lista por pares. Fiquei me perguntando qual seria a maneira pitônica e eficiente de fazê-lo, e encontrei isso no Google:

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

Eu pensei que era pitônico o suficiente, mas depois de uma discussão recente envolvendoidiomas versus eficiência, Decidi fazer alguns testes:

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

Estes foram os resultados no meu computador:

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

Se eu os estiver interpretando corretamente, isso significa que a implementação de listas, indexação de listas e fatiamento de listas no Python é muito eficiente. É um resultado reconfortante e inesperado.

Existe outra maneira "melhor" de percorrer uma lista em pares?

Observe que, se a lista tiver um número ímpar de elementos, o último não estará em nenhum dos pares.

Qual seria o caminho certo para garantir que todos os elementos sejam incluídos?

Eu adicionei essas duas sugestões das respostas para os testes:

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

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

Estes são os resultados:

0.00159502029419 1.25745987892 1.25586485863
0.00222492218018 1.23795199394 1.23572707176
Resultados até agora

Mais pitônico e muito eficiente:

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

Mais eficiente e muito pitônico:

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

Levei um momento para pensar que a primeira resposta usa dois iteradores enquanto a segunda usa um único.

Para lidar com seqüências com um número ímpar de elementos, a sugestão foi aumentar a sequência original, adicionando um elemento (None) que é emparelhado com o último elemento anterior, algo que pode ser alcançado comitertools.izip_longest().

Finalmente

Observe que, no Python 3.x,zip() comporta-se comoitertools.izip()eitertools.izip() se foi.

questionAnswers(6)

yourAnswerToTheQuestion