Às vezes, eu preciso iterar uma lista no Python, olhando para o elemento "atual" e o elemento "próximo". Até agora, fiz isso com código como:
for current, next in zip(the_list, the_list[1:]):
# Do something
Isso funciona e faz o que eu espero, mas há uma maneira mais idiomática ou eficiente de fazer a mesma coisa?
next
esse método mascara um built-in.next
é também um built-in função em Python 2.Respostas:
Aqui está um exemplo relevante dos documentos do módulo itertools :
Para Python 2, você precisa, em
itertools.izip
vez dezip
:Como isso funciona:
Primeiro, dois iteradores paralelos
a
eb
são criados (atee()
chamada), ambos apontando para o primeiro elemento do iterável original. O segundo iteradorb
é movido 1 passo à frente (anext(b, None)
) chamada. Nesse ponto,a
aponte para s0 eb
aponte para s1. Ambosa
eb
podem atravessar o iterador original independentemente - a função izip pega os dois iteradores e faz pares dos elementos retornados, avançando os dois iteradores no mesmo ritmo.Uma ressalva: a
tee()
função produz dois iteradores que podem avançar independentemente um do outro, mas tem um custo. Se um dos iteradores avançar mais que o outro, serátee()
necessário manter os elementos consumidos na memória até o segundo iterador consumi-los também (ele não pode 'rebobinar' o iterador original). Aqui não importa, porque um iterador está apenas um passo à frente do outro, mas, em geral, é fácil usar muita memória dessa maneira.E como
tee()
pode assumir umn
parâmetro, isso também pode ser usado para mais de dois iteradores paralelos:fonte
zip(ł, ł[1:])
é muito mais curto e pythônicofuncy
módulo:funcy.pairwise
: funcy.readthedocs.io/en/stable/seqs.html#pairwiseRoll your own!
fonte
Como,
the_list[1:]
na verdade, cria uma cópia da lista inteira (excluindo o primeiro elemento) ezip()
cria uma lista de tuplas imediatamente quando chamada, no total, três cópias da sua lista são criadas. Se sua lista é muito grande, você pode preferirque não copia a lista.
fonte
the_list[1:]
apenas cria um objeto de fatia em vez de uma cópia de quase toda a lista - portanto, a técnica do OP não é tão inútil quanto você soa.[1:]
cria o objeto de fatia (ou possivelmente "1:
"), que é passado para__slice__
a lista, que retorna uma cópia contendo apenas os elementos selecionados. Uma forma idiomática para copiar uma lista él_copy = l[:]
(o que eu acho feia e ilegível - preferirl_copy = list(l)
)__slice__
um método especial.the_list[1:]
é equivalente athe_list[slice(1, None)]
, que por sua vez é equivalente alist.__getitem__(the_list, slice(1, None))
.the_list[1:]
é apenas uma cópia superficial, portanto consiste em apenas um ponteiro por item da lista. A parte que consome mais memória é azip()
própria, porque criará uma lista de umatuple
instância por item de lista, cada uma contendo dois ponteiros para os dois itens e algumas informações adicionais. Esta lista consumirá nove vezes a quantidade de memória[1:]
consumida pela cópia .Estou apenas divulgando isso, estou muito surpreso que ninguém tenha pensado em enumerar ().
fonte
if
também pode ser removido se você usar a divisão:for (index, thing) in enumerate(the_list[:-1]): current, next_ = thing, the_list[index + 1]
A iteração por índice pode fazer a mesma coisa:
Resultado:
fonte
i
seja sempre o índice do elemento atual.Agora é uma importação simples A partir de 16 de maio de 2020
Documentos para more-itertools Sob o capô, esse código é o mesmo das outras respostas, mas prefiro importações quando disponíveis.
Se você ainda não o instalou, então:
pip install more-itertools
Exemplo
Por exemplo, se você tivesse a sequência fibbonnacci, poderia calcular as proporções dos pares subsequentes como:
fonte
Pares de uma lista usando uma compreensão de lista
Resultado:
fonte
Estou realmente surpreso que ninguém tenha mencionado a solução mais curta, mais simples e mais importante em geral :
Python 3:
Python 2:
Ele funciona para iteração em pares passando
n=2
, mas pode lidar com qualquer número maior:fonte
Uma solução básica:
fonte
fonte