Muitas vezes, descobri a necessidade de processar uma lista por pares. Eu estava me perguntando qual seria a maneira pítônica e eficiente de fazer isso, e encontrei isso no Google:
pairs = zip(t[::2], t[1::2])
Achei que era pythônico o suficiente, mas depois de uma discussão recente envolvendo expressões idiomáticas 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 estou interpretando-os corretamente, isso deve significar que a implementação de listas, indexação de lista e divisão de lista em 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 a maneira certa de garantir que todos os elementos sejam incluídos?
Eu adicionei estas duas sugestões das respostas aos 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 grocar que a primeira resposta usa dois iteradores enquanto a segunda usa um único.
Para lidar com sequê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 itertools.izip_longest()
.
Finalmente
Observe que, no Python 3.x, zip()
se comporta como itertools.izip()
e itertools.izip()
desaparece.
timeit
módulo.Respostas:
Minha maneira favorita de fazer isso:
Quando você deseja emparelhar todos os elementos, obviamente pode precisar de um fillvalue:
fonte
itertools
seção de receitas.izip(*[iter(t)]*size)
Eu diria que sua solução inicial
pairs = zip(t[::2], t[1::2])
é a melhor porque é mais fácil de ler (e em Python 3,zip
retorna automaticamente um iterador em vez de uma lista).Para garantir que todos os elementos sejam incluídos, você pode simplesmente estender a lista
None
.Então, se a lista tiver um número ímpar de elementos, o último par será
(item, None)
.fonte
Eu começo com um pequeno aviso - não use o código abaixo. Não é Pythônico de forma alguma, eu escrevi apenas para me divertir. É semelhante à
pairwise
função @ THC4k , mas usaiter
e fechalambda
. Não usaitertools
módulo e não suportafillvalue
. Eu coloquei aqui porque alguém pode achar interessante:fonte
No que diz respeito à maioria dos pythônicos, eu diria que as receitas fornecidas nos documentos fonte do python (algumas das quais se parecem muito com as respostas que @JochenRitzel forneceu) são provavelmente sua melhor aposta;)
fonte
Não posso dizer com certeza, mas duvido: qualquer outro percurso incluiria mais código Python que precisa ser interpretado. As funções embutidas como zip () são escritas em C, que é muito mais rápido.
Verifique o comprimento da lista e se for ímpar (
len(list) & 1 == 1
), copie a lista e anexe um item.fonte
fonte
Apenas faça isso:
fonte
list(zip(l, l[1:]))
e não divide a lista em pares.Aqui está um exemplo de criação de pares / pernas usando um gerador. Geradores estão livres de limites de pilha
Exemplo:
Resultado:
fonte
[(0, 1), (2, 3), (4, 5)....
zip()
já retorna um gerador em Python 3.x, @VladBezdenCaso alguém precise do algoritmo de resposta, aqui está:
Mas observe que sua lista original também será reduzida ao último elemento, porque você usou
pop
nela.fonte