Iterando sobre cada dois elementos em uma lista


Respostas:


231

Você precisa de uma pairwise()(ou grouped()) implementação.

Para Python 2:

from itertools import izip

def pairwise(iterable):
    "s -> (s0, s1), (s2, s3), (s4, s5), ..."
    a = iter(iterable)
    return izip(a, a)

for x, y in pairwise(l):
   print "%d + %d = %d" % (x, y, x + y)

Ou, de maneira mais geral:

from itertools import izip

def grouped(iterable, n):
    "s -> (s0,s1,s2,...sn-1), (sn,sn+1,sn+2,...s2n-1), (s2n,s2n+1,s2n+2,...s3n-1), ..."
    return izip(*[iter(iterable)]*n)

for x, y in grouped(l, 2):
   print "%d + %d = %d" % (x, y, x + y)

No Python 3, você pode substituir izippela zip()função interna e soltar o import.

Todo o crédito a martineau por sua resposta à minha pergunta , eu achei isso muito eficiente, pois itera apenas uma vez na lista e não cria nenhuma lista desnecessária no processo.

NB : Isso não deve ser confundido com a pairwisereceita na própria itertoolsdocumentação do Python , que produz s -> (s0, s1), (s1, s2), (s2, s3), ..., como apontado por @lazyr nos comentários.

Pouco acréscimo para quem gostaria de verificar o tipo com mypy no Python 3:

from typing import Iterable, Tuple, TypeVar

T = TypeVar("T")

def grouped(iterable: Iterable[T], n=2) -> Iterable[Tuple[T, ...]]:
    """s -> (s0,s1,s2,...sn-1), (sn,sn+1,sn+2,...s2n-1), ..."""
    return zip(*[iter(iterable)] * n)

16
Não deve ser confundido com a função de pares sugeridos nas itertools seção de receitas, que os rendimentoss -> (s0,s1), (s1,s2), (s2, s3), ...
Lauritz V. Thaulow

1
Faz uma coisa diferente. Sua versão gera apenas metade do número de pares em comparação com a itertoolsfunção de receita com o mesmo nome. Claro que o seu é mais rápido ...
Sven Marnach

Hã? Sua função e a função a que me referi fazem coisas diferentes, e esse foi o ponto do meu comentário.
Lauritz V. Thaulow 22/03

5
SEJA CUIDADOSO! O uso dessas funções coloca o risco de não repetir os últimos elementos de um iterável. Exemplo: list (agrupado ([1,2,3], 2)) >>> [(1, 2)] .. quando você espera [(1,2), (3,)]
egafni

4
@ Erik49: No caso especificado na pergunta, não faria sentido ter uma tupla 'incompleta'. Se você deseja incluir uma tupla incompleta, pode usar em izip_longest()vez de izip(). Por exemplo: list(izip_longest(*[iter([1, 2, 3])]*2, fillvalue=0))-> [(1, 2), (3, 0)]. Espero que isto ajude.
Johnsyweb

191

Bem, você precisa de uma tupla de 2 elementos, então

data = [1,2,3,4,5,6]
for i,k in zip(data[0::2], data[1::2]):
    print str(i), '+', str(k), '=', str(i+k)

Onde:

  • data[0::2] significa criar uma coleção de subconjuntos de elementos que (index % 2 == 0)
  • zip(x,y) cria uma coleção de tupla a partir de coleções x e y dos mesmos elementos de índice.

8
Isso também pode ser estendido caso sejam necessários mais de dois elementos. Por exemplofor i, j, k in zip(data[0::3], data[1::3], data[2::3]):
lifebalance

19
Muito mais limpo do que extrair uma importação e definir uma função!
Kmarsh

7
@ kmarsh: Mas isso só funciona em seqüências, a função funciona em qualquer iterável; e isso usa O (N) espaço extra, a função não; por outro lado, isso geralmente é mais rápido. Existem boas razões para escolher um ou outro; ter medo importnão é um deles.
abarnert

77
>>> l = [1,2,3,4,5,6]

>>> zip(l,l[1:])
[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]

>>> zip(l,l[1:])[::2]
[(1, 2), (3, 4), (5, 6)]

>>> [a+b for a,b in zip(l,l[1:])[::2]]
[3, 7, 11]

>>> ["%d + %d = %d" % (a,b,a+b) for a,b in zip(l,l[1:])[::2]]
['1 + 2 = 3', '3 + 4 = 7', '5 + 6 = 11']

1
Isto não está trabalhando em Python-3.6.0, mas ainda trabalhando em Python-2.7.10
Hamid Rohani

6
@HamidRohani zipretorna um zipobjeto no Python 3, que não pode ser subscrito. Ele precisa ser convertido em uma sequência ( list, tupleetc.) primeiro, mas "não está funcionando" é um pouco exagerado.
vaultah

58

Uma solução simples.

l = [1, 2, 3, 4, 5, 6]

para i no intervalo (0, len (l), 2):
    print str (l [i]), '+', str (l [i + 1]), '=', str (l [i] + l [i + 1])

1
e se sua lista não for uniforme e você quiser apenas mostrar o último número como está?
Hans de Jong

@HansdeJong não entendeu você. Por favor, explique um pouco mais.
taskinoor

2
Obrigado. Eu já descobri como fazê-lo. O problema era que, se você tivesse uma lista que não tinha nem números, ela receberia um erro de índice. Resolvido com uma tentativa: exceto:
Hans de Jong

Ou, ((l[i], l[i+1])for i in range(0, len(l), 2))para um gerador, pode ser facilmente modificado para tuplas mais longas.
Basel Shishani

44

Embora todas as respostas zipestejam corretas, acho que a implementação da funcionalidade leva a um código mais legível:

def pairwise(it):
    it = iter(it)
    while True:
        try:
            yield next(it), next(it)
        except StopIteration:
            # no more elements in the iterator
            return

A it = iter(it)parte garante que itna verdade seja um iterador, não apenas um iterável. Se itjá é um iterador, esta linha é um no-op.

Uso:

for a, b in pairwise([0, 1, 2, 3, 4, 5]):
    print(a + b)

2
Esta solução permite a generalização para o tamanho de tuplas> 2
guilloptero

1
Essa solução também funciona se itfor apenas um iterador e não iterável. As outras soluções parecem depender da possibilidade de criar dois iteradores independentes para a sequência.
skyking 4/09/15

Encontrei essa abordagem em stackoverflow.com/a/16815056/2480481 antes de ver esta resposta. É mais limpo, mais fácil do que lidar com zip ().
M3nda

2
Gosto que ele permita evitar triplicar o uso da memória como resposta aceita.
Kentzo 13/08/18

Isso não funciona bem com forloops no Python 3.5+ devido ao PEP 479 , que substitui qualquer um StopIterationgerado em um gerador por a RuntimeError.
sidney

28

Espero que seja uma maneira ainda mais elegante de fazer isso.

a = [1,2,3,4,5,6]
zip(a[::2], a[1::2])

[(1, 2), (3, 4), (5, 6)]

18

Caso você esteja interessado no desempenho, fiz uma pequena referência (usando minha biblioteca simple_benchmark) para comparar o desempenho das soluções e incluí uma função em um dos meus pacotes:iteration_utilities.grouper

from iteration_utilities import grouper
import matplotlib as mpl
from simple_benchmark import BenchmarkBuilder

bench = BenchmarkBuilder()

@bench.add_function()
def Johnsyweb(l):
    def pairwise(iterable):
        "s -> (s0, s1), (s2, s3), (s4, s5), ..."
        a = iter(iterable)
        return zip(a, a)

    for x, y in pairwise(l):
        pass

@bench.add_function()
def Margus(data):
    for i, k in zip(data[0::2], data[1::2]):
        pass

@bench.add_function()
def pyanon(l):
    list(zip(l,l[1:]))[::2]

@bench.add_function()
def taskinoor(l):
    for i in range(0, len(l), 2):
        l[i], l[i+1]

@bench.add_function()
def mic_e(it):
    def pairwise(it):
        it = iter(it)
        while True:
            try:
                yield next(it), next(it)
            except StopIteration:
                return

    for a, b in pairwise(it):
        pass

@bench.add_function()
def MSeifert(it):
    for item1, item2 in grouper(it, 2):
        pass

bench.use_random_lists_as_arguments(sizes=[2**i for i in range(1, 20)])
benchmark_result = bench.run()
mpl.rcParams['figure.figsize'] = (8, 10)
benchmark_result.plot_both(relative_to=MSeifert)

insira a descrição da imagem aqui

Portanto, se você deseja a solução mais rápida sem dependências externas, provavelmente deve usar a abordagem dada por Johnysweb (no momento da redação deste documento é a resposta mais votada e aceita).

Se você não se importa com a dependência adicional, o grouperfrom iteration_utilitiesprovavelmente será um pouco mais rápido.

Pensamentos adicionais

Algumas das abordagens têm algumas restrições, que não foram discutidas aqui.

Por exemplo, algumas soluções funcionam apenas para sequências (ou seja, listas, seqüências de caracteres etc.), por exemplo, Margus / pyanon / taskino ou soluções que usam indexação, enquanto outras soluções funcionam em qualquer iterável (ou seja, sequências e geradores, iteradores) como Johnysweb / mic_e / minhas soluções.

Então, Johnysweb também forneceu uma solução que funciona para outros tamanhos além de 2, enquanto as outras respostas não (ok, iteration_utilities.groupertambém permite definir o número de elementos para "agrupar").

Depois, há também a pergunta sobre o que deve acontecer se houver um número ímpar de elementos na lista. O item restante deve ser descartado? A lista deve ser preenchida para torná-la uniforme? O item restante deve ser devolvido como único? A outra resposta não aborda esse ponto diretamente, no entanto, se eu não ignorei nada, todos eles seguem a abordagem de que o item restante deve ser descartado (exceto as respostas dos executores de tarefas - que realmente geram uma exceção).

Com groupervocê pode decidir o que deseja fazer:

>>> from iteration_utilities import grouper

>>> list(grouper([1, 2, 3], 2))  # as single
[(1, 2), (3,)]

>>> list(grouper([1, 2, 3], 2, truncate=True))  # ignored
[(1, 2)]

>>> list(grouper([1, 2, 3], 2, fillvalue=None))  # padded
[(1, 2), (3, None)]

12

Use os comandos zipe iterjuntos:

Acho esta solução usando iterpara ser bastante elegante:

it = iter(l)
list(zip(it, it))
# [(1, 2), (3, 4), (5, 6)]

Que encontrei na documentação zip do Python 3 .

it = iter(l)
print(*(f'{u} + {v} = {u+v}' for u, v in zip(it, it)), sep='\n')

# 1 + 2 = 3
# 3 + 4 = 7
# 5 + 6 = 11

Para generalizar para Nelementos de cada vez:

N = 2
list(zip(*([iter(l)] * N)))
# [(1, 2), (3, 4), (5, 6)]

10
for (i, k) in zip(l[::2], l[1::2]):
    print i, "+", k, "=", i+k

zip(*iterable) retorna uma tupla com o próximo elemento de cada iterável.

l[::2] retorna o primeiro, o terceiro, o quinto, etc. elementos da lista: o primeiro dois pontos indica que a fatia começa no início porque não há número atrás dela, o segundo dois pontos é necessário apenas se você quiser um 'passo na fatia '(neste caso 2).

l[1::2]faz a mesma coisa, mas começa no segundo elemento da lista e, portanto, retorna o segundo, o quarto, o sexto, etc. elemento da lista original .


4
Essa resposta já foi dada por Margus há dois anos. stackoverflow.com/questions/5389507/...
cababunga

1
1 para explicar como a [number::number]sintaxe funciona. útil para quem não usar python muitas vezes
Alby

2

Com a desembalagem:

l = [1,2,3,4,5,6]
while l:
    i, k, *l = l
    print(str(i), '+', str(k), '=', str(i+k))

Uau! Por que eu não conseguia pensar nisso :) Você precisa apenas lidar com o caso em que não há par absoluto (entradas ímpares)
Saurav Kumar

1

Para qualquer um que possa ajudar, aqui está uma solução para um problema semelhante, mas com pares sobrepostos (em vez de pares mutuamente exclusivos).

Na documentação dos instrumentos do Python :

from itertools import izip

def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return izip(a, b)

Ou, de maneira mais geral:

from itertools import izip

def groupwise(iterable, n=2):
    "s -> (s0,s1,...,sn-1), (s1,s2,...,sn), (s2,s3,...,sn+1), ..."
    t = tee(iterable, n)
    for i in range(1, n):
        for j in range(0, i):
            next(t[i], None)
    return izip(*t)

1

você pode usar o pacote more_itertools .

import more_itertools

lst = range(1, 7)
for i, j in more_itertools.chunked(lst, 2):
    print(f'{i} + {j} = {i+j}')

1

Eu preciso dividir uma lista por um número e corrigido assim.

l = [1,2,3,4,5,6]

def divideByN(data, n):
        return [data[i*n : (i+1)*n] for i in range(len(data)//n)]  

>>> print(divideByN(l,2))
[[1, 2], [3, 4], [5, 6]]

>>> print(divideByN(l,3))
[[1, 2, 3], [4, 5, 6]]

1

Há muitas maneiras de fazer isso. Por exemplo:

lst = [1,2,3,4,5,6]
[(lst[i], lst[i+1]) for i,_ in enumerate(lst[:-1])]    
>>>[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]

[i for i in zip(*[iter(lst)]*2)]    
>>>[(1, 2), (3, 4), (5, 6)]

0

Pensei que este é um bom lugar para compartilhar minha generalização disso para n> 2, que é apenas uma janela deslizante sobre um iterável:

def sliding_window(iterable, n):
    its = [ itertools.islice(iter, i, None) 
            for i, iter
            in enumerate(itertools.tee(iterable, n)) ]                               

    return itertools.izip(*its)

0

O título desta pergunta é enganoso, você parece estar procurando pares consecutivos, mas se quiser iterar sobre o conjunto de todos os pares possíveis, isso funcionará:

for i,v in enumerate(items[:-1]):
        for u in items[i+1:]:

0

Usando a digitação para verificar dados usando a ferramenta de análise estática mypy :

from typing import Iterator, Any, Iterable, TypeVar, Tuple

T_ = TypeVar('T_')
Pairs_Iter = Iterator[Tuple[T_, T_]]

def legs(iterable: Iterator[T_]) -> Pairs_Iter:
    begin = next(iterable)
    for end in iterable:
        yield begin, end
        begin = end

0

Uma abordagem simplista:

[(a[i],a[i+1]) for i in range(0,len(a),2)]

isso é útil se sua matriz é a e você deseja iterá-la por pares. Para iterar em trigêmeos ou mais, basta alterar o comando da etapa "range", por exemplo:

[(a[i],a[i+1],a[i+2]) for i in range(0,len(a),3)]

(você precisa lidar com valores excedentes se o comprimento da matriz e a etapa não couberem)


-1

Aqui podemos ter um alt_elemmétodo que pode caber no seu loop for.

def alt_elem(list, index=2):
    for i, elem in enumerate(list, start=1):
        if not i % index:
           yield tuple(list[i-index:i])


a = range(10)
for index in [2, 3, 4]:
    print("With index: {0}".format(index))
    for i in alt_elem(a, index):
       print(i)

Resultado:

With index: 2
(0, 1)
(2, 3)
(4, 5)
(6, 7)
(8, 9)
With index: 3
(0, 1, 2)
(3, 4, 5)
(6, 7, 8)
With index: 4
(0, 1, 2, 3)
(4, 5, 6, 7)

Nota: A solução acima pode não ser eficiente, considerando as operações executadas em func.


-1
a_list = [1,2,3,4,5,6]
empty_list = [] 
for i in range(0,len(a_list),2):
   empty_list.append(a_list[i]+a_list[i+1])   
print(empty_list)
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.