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 importIterable,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)
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.
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']
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])
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)whileTrue:try:yield next(it), next(it)exceptStopIteration:# no more elements in the iteratorreturn
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.
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.
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 importBenchmarkBuilder
bench =BenchmarkBuilder()@bench.add_function()defJohnsyweb(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()defMargus(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)whileTrue:try:yield next(it), next(it)exceptStopIteration:returnfor a, b in pairwise(it):pass@bench.add_function()defMSeifert(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)
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).
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 .
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).
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]]
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)]
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:]:
Usando a digitação para verificar dados usando a ferramenta de análise estática mypy :
from typing importIterator,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
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)
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):ifnot 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.
Respostas:
Você precisa de uma
pairwise()
(ougrouped()
) implementação.Para Python 2:
Ou, de maneira mais geral:
No Python 3, você pode substituir
izip
pelazip()
função interna e soltar oimport
.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
pairwise
receita na própriaitertools
documentação do Python , que produzs -> (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:
fonte
s -> (s0,s1), (s1,s2), (s2, s3), ...
itertools
função de receita com o mesmo nome. Claro que o seu é mais rápido ...izip_longest()
vez deizip()
. Por exemplo:list(izip_longest(*[iter([1, 2, 3])]*2, fillvalue=0))
->[(1, 2), (3, 0)]
. Espero que isto ajude.Bem, você precisa de uma tupla de 2 elementos, então
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.fonte
for i, j, k in zip(data[0::3], data[1::3], data[2::3]):
import
não é um deles.fonte
zip
retorna umzip
objeto no Python 3, que não pode ser subscrito. Ele precisa ser convertido em uma sequência (list
,tuple
etc.) primeiro, mas "não está funcionando" é um pouco exagerado.Uma solução simples.
fonte
((l[i], l[i+1])for i in range(0, len(l), 2))
para um gerador, pode ser facilmente modificado para tuplas mais longas.Embora todas as respostas
zip
estejam corretas, acho que a implementação da funcionalidade leva a um código mais legível:A
it = iter(it)
parte garante queit
na verdade seja um iterador, não apenas um iterável. Seit
já é um iterador, esta linha é um no-op.Uso:
fonte
it
for 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.for
loops no Python 3.5+ devido ao PEP 479 , que substitui qualquer umStopIteration
gerado em um gerador por aRuntimeError
.Espero que seja uma maneira ainda mais elegante de fazer isso.
fonte
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
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
grouper
fromiteration_utilities
provavelmente 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.grouper
també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
grouper
você pode decidir o que deseja fazer:fonte
Use os comandos
zip
eiter
juntos:Acho esta solução usando
iter
para ser bastante elegante:Que encontrei na documentação zip do Python 3 .
Para generalizar para
N
elementos de cada vez:fonte
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 .fonte
[number::number]
sintaxe funciona. útil para quem não usar python muitas vezesCom a desembalagem:
fonte
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 :
Ou, de maneira mais geral:
fonte
você pode usar o pacote more_itertools .
fonte
Eu preciso dividir uma lista por um número e corrigido assim.
fonte
Há muitas maneiras de fazer isso. Por exemplo:
fonte
Pensei que este é um bom lugar para compartilhar minha generalização disso para n> 2, que é apenas uma janela deslizante sobre um iterável:
fonte
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á:
fonte
Usando a digitação para verificar dados usando a ferramenta de análise estática mypy :
fonte
Uma abordagem simplista:
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:
(você precisa lidar com valores excedentes se o comprimento da matriz e a etapa não couberem)
fonte
Aqui podemos ter um
alt_elem
método que pode caber no seu loop for.Resultado:
Nota: A solução acima pode não ser eficiente, considerando as operações executadas em func.
fonte
fonte