Come faccio a creare un forciclo o una comprensione dell'elenco in modo che ogni iterazione mi dia due elementi?
l = [1,2,3,4,5,6]
for i,k in ???:
print str(i), '+', str(k), '=', str(i+k)
Produzione:
1+2=3
3+4=7
5+6=11
Come faccio a creare un forciclo o una comprensione dell'elenco in modo che ogni iterazione mi dia due elementi?
l = [1,2,3,4,5,6]
for i,k in ???:
print str(i), '+', str(k), '=', str(i+k)
Produzione:
1+2=3
3+4=7
5+6=11
Risposte:
È necessaria un'implementazione pairwise()(o grouped()).
Per 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)
O, più in generale:
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)
In Python 3, puoi sostituirlo izipcon la zip()funzione integrata e rilasciare il fileimport .
Tutto merito a martineau per la sua risposta alla mia domanda , ho trovato che questo è molto efficace in quanto scorre solo una volta nell'elenco e non crea alcun elenco non necessario nel processo.
NB : Questo non dovrebbe essere confuso con la pairwisericetta nella itertoolsdocumentazione di Python , che produce s -> (s0, s1), (s1, s2), (s2, s3), ..., come sottolineato da @lazyr nei commenti.
Piccola aggiunta per coloro che vorrebbero fare il controllo del tipo con Mypy su 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)
s -> (s0,s1), (s1,s2), (s2, s3), ...
itertoolsfunzione ricetta con lo stesso nome. Ovviamente il tuo è più veloce ...
izip_longest()invece di izip(). Ad esempio: list(izip_longest(*[iter([1, 2, 3])]*2, fillvalue=0))-> [(1, 2), (3, 0)]. Spero che questo ti aiuti.
Bene, hai bisogno di una tupla di 2 elementi, quindi
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)
Dove:
data[0::2] significa creare sottoinsieme di elementi che (index % 2 == 0)zip(x,y) crea una raccolta tupla dagli stessi elementi indice delle raccolte xey.for i, j, k in zip(data[0::3], data[1::3], data[2::3]):
importnon è uno di questi.
>>> 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']
ziprestituisce un zipoggetto in Python 3, che non è sottoscrivibile. Deve essere convertito in una sequenza ( list, tupleecc) prima, ma "non lavoro" è un po 'un tratto.
Una soluzione semplice
l = [1, 2, 3, 4, 5, 6]
per i nell'intervallo (0, len (l), 2):
print str (l [i]), '+', str (l [i + 1]), '=', str (l [i] + l [i + 1])
((l[i], l[i+1])for i in range(0, len(l), 2))per un generatore, può essere facilmente modificato per tuple più lunghe.
Sebbene tutte le risposte utilizzate zipsiano corrette, trovo che l'implementazione della funzionalità porti a un codice più leggibile:
def pairwise(it):
it = iter(it)
while True:
try:
yield next(it), next(it)
except StopIteration:
# no more elements in the iterator
return
La it = iter(it)parte garantisce che in itrealtà è un iteratore, non solo un iterabile. Se itè già un iteratore, questa riga è no-op.
Uso:
for a, b in pairwise([0, 1, 2, 3, 4, 5]):
print(a + b)
itè solo un iteratore e non un iterabile. Le altre soluzioni sembrano basarsi sulla possibilità di creare due iteratori indipendenti per la sequenza.
Spero che questo sia un modo ancora più elegante di farlo.
a = [1,2,3,4,5,6]
zip(a[::2], a[1::2])
[(1, 2), (3, 4), (5, 6)]
Nel caso in cui tu sia interessato alle prestazioni, ho fatto un piccolo benchmark (usando la mia libreria simple_benchmark) per confrontare le prestazioni delle soluzioni e ho incluso una funzione da uno dei miei pacchetti: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)
Quindi se vuoi la soluzione più veloce senza dipendenze esterne, probabilmente dovresti semplicemente usare l'approccio dato da Johnysweb (al momento della stesura è la risposta più votata e accettata).
Se non ti dispiace la dipendenza aggiuntiva , probabilmente il grouperda iteration_utilitiessarà un po 'più veloce.
Alcuni approcci hanno alcune restrizioni, che non sono state discusse qui.
Ad esempio alcune soluzioni funzionano solo per le sequenze (ovvero elenchi, stringhe, ecc.), Ad esempio le soluzioni Margus / pyanon / taskinoor che utilizzano l'indicizzazione mentre altre soluzioni funzionano su qualsiasi iterabile (ovvero sequenze e generatori, iteratori) come Johnysweb / mic_e / mie soluzioni.
Quindi Johnysweb ha anche fornito una soluzione che funziona per dimensioni diverse da 2, mentre le altre risposte no (okay, iteration_utilities.grouperconsente anche di impostare il numero di elementi su "gruppo").
Quindi c'è anche la domanda su cosa dovrebbe accadere se ci fosse un numero dispari di elementi nell'elenco. L'articolo rimanente dovrebbe essere respinto? L'elenco dovrebbe essere imbottito per renderlo uniforme? L'articolo rimanente deve essere restituito come singolo? L'altra risposta non affronta direttamente questo punto, tuttavia, se non ho trascurato nulla, tutti seguono l'approccio secondo cui l'elemento rimanente dovrebbe essere respinto (ad eccezione della risposta di taskinoors - che solleverà effettivamente un'eccezione).
Con grouperte puoi decidere cosa vuoi fare:
>>> 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)]
Utilizzare i comandi zipe iterinsieme:
Trovo che questa soluzione itersia piuttosto elegante:
it = iter(l)
list(zip(it, it))
# [(1, 2), (3, 4), (5, 6)]
Che ho trovato nella documentazione zip di 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
Per generalizzare agli Nelementi alla volta:
N = 2
list(zip(*([iter(l)] * N)))
# [(1, 2), (3, 4), (5, 6)]
for (i, k) in zip(l[::2], l[1::2]):
print i, "+", k, "=", i+k
zip(*iterable) restituisce una tupla con l'elemento successivo di ogni iterabile.
l[::2] restituisce il 1o, 3o, 5o, ecc. elemento dell'elenco: il primo punto indica che la sezione inizia all'inizio perché non c'è un numero dietro, il secondo punto è necessario solo se si desidera un 'passaggio nella sezione '(in questo caso 2).
l[1::2]fa la stessa cosa ma inizia nel secondo elemento degli elenchi, quindi restituisce il secondo, il quarto, il sesto elemento, ecc. dell'elenco originale .
[number::number]funziona la sintassi. utile per chi non usa spesso Python
puoi usare il pacchetto more_itertools .
import more_itertools
lst = range(1, 7)
for i, j in more_itertools.chunked(lst, 2):
print(f'{i} + {j} = {i+j}')
Con disimballaggio:
l = [1,2,3,4,5,6]
while l:
i, k, *l = l
print(str(i), '+', str(k), '=', str(i+k))
Per chiunque possa aiutare, ecco una soluzione a un problema simile ma con coppie sovrapposte (anziché coppie reciprocamente esclusive).
Dalla documentazione di Python itertools :
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)
O, più in generale:
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)
Devo dividere un elenco per un numero e corretto in questo modo.
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]]
Ci sono molti modi per farlo. Per esempio:
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)]
Ho pensato che questo è un buon posto per condividere la mia generalizzazione di questo per n> 2, che è solo una finestra scorrevole su un iterabile:
def sliding_window(iterable, n):
its = [ itertools.islice(iter, i, None)
for i, iter
in enumerate(itertools.tee(iterable, n)) ]
return itertools.izip(*its)
Utilizzando la digitazione in modo da poter verificare i dati utilizzando lo strumento di analisi statica 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
Un approccio semplicistico:
[(a[i],a[i+1]) for i in range(0,len(a),2)]
questo è utile se l'array è a e si desidera iterarlo su coppie. Per iterare su terzine o altro basta cambiare il comando "range" step, ad esempio:
[(a[i],a[i+1],a[i+2]) for i in range(0,len(a),3)]
(devi affrontare i valori in eccesso se la lunghezza dell'array e il passaggio non si adattano)
from itertools import tee
def pairwise(iterable):
a = iter(iterable)
for i in a:
try:
yield (i, next(a))
except StopIteration:
yield(i, None)
for i in pairwise([3, 7, 8, 9, 90, 900]):
print(i)
Produzione:
(3, 7)
(8, 9)
(90, 900)
>
Qui possiamo avere un alt_elemmetodo che può adattarsi al tuo ciclo 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)
Produzione:
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: la soluzione sopra potrebbe non essere efficiente considerando le operazioni eseguite in func.