Scorrendo ogni due elementi in un elenco


209

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

2
Per la sovrapposizione coppia: stackoverflow.com/questions/5434891/...
user202729

Risposte:


231

È 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)

16
Da non confondere con la funzione a coppie suggerita nella sezione ricette itertools , che produces -> (s0,s1), (s1,s2), (s2, s3), ...
Lauritz V. Thaulow

1
Fa una cosa diversa. La tua versione produce solo la metà del numero di coppie rispetto alla itertoolsfunzione ricetta con lo stesso nome. Ovviamente il tuo è più veloce ...
Sven Marnach il

Eh? La tua funzione e la funzione a cui mi riferivo fanno cose diverse, e quello era il punto del mio commento.
Lauritz V. Thaulow,

5
STAI ATTENTO! L'uso di queste funzioni comporta il rischio di non ripetere gli ultimi elementi di un iterabile. Esempio: list (raggruppato ([1,2,3], 2)) >>> [(1, 2)] .. quando ti aspetteresti [(1,2), (3,)]
egafni

4
@ Erik49: Nel caso specificato nella domanda, non avrebbe senso avere una tupla 'incompleta'. Se si desidera includere una tupla incompleta, è possibile utilizzare 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.
Johnsyweb,

191

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.

8
Questo può anche essere esteso nel caso siano necessari più di due elementi. Ad esempiofor i, j, k in zip(data[0::3], data[1::3], data[2::3]):
lifebalance

19
Molto più pulito che inserire un'importazione e definire una funzione!
kmarsh

7
@kmarsh: Ma questo funziona solo su sequenze, la funzione funziona su qualsiasi iterabile; e questo utilizza O (N) spazio extra, la funzione no; d'altra parte, questo è generalmente più veloce. Ci sono buoni motivi per scegliere l'uno o l'altro; avere paura importnon è uno di questi.
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
Questo non funziona su Python-3.6.0 ma funziona ancora su Python-2.7.10
Hamid Rohani

6
@HamidRohani 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.
vaultah,

58

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])

1
cosa succede se la tua lista non è pari e vuoi mostrare solo l'ultimo numero così com'è?
Hans de Jong,

@HansdeJong non ti ha preso. Per favore, spiega un po 'di più.
taskinoor

2
Grazie. Ho già capito come farlo. Il problema era che se avessi un elenco che non conteneva nemmeno una quantità di numeri, si otterrebbe un errore di indice. Risolto con un tentativo: tranne:
Hans de Jong,

O ((l[i], l[i+1])for i in range(0, len(l), 2))per un generatore, può essere facilmente modificato per tuple più lunghe.
Basilea Shishani,

44

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)

2
Questa soluzione consente di generalizzare a dimensioni di tuple> 2
guilloptero,

1
Questa soluzione funziona anche se itè solo un iteratore e non un iterabile. Le altre soluzioni sembrano basarsi sulla possibilità di creare due iteratori indipendenti per la sequenza.
stelle

Ho trovato questo approccio su stackoverflow.com/a/16815056/2480481 prima di vedere questa risposta. È più pulito, più semplice rispetto a zip ().
m3nda,

2
Mi piace che consenta di evitare di triplicare l'utilizzo della memoria come risposta accettata.
Kentzo,

Questo non funziona bene con i forloop in Python 3.5+ a causa di PEP 479 , che sostituisce qualsiasi StopIterationsollevato in un generatore con a RuntimeError.
Sidney,

28

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)]

18

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)

inserisci qui la descrizione dell'immagine

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.

Pensieri aggiuntivi

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)]

12

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)]

10
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 .


4
Questa risposta è stata già data da Margus due anni fa. stackoverflow.com/questions/5389507/...
cababunga

1
1 per spiegare come [number::number]funziona la sintassi. utile per chi non usa spesso Python
Alby il

2

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}')

2

Con disimballaggio:

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

Wow! Perché non riuscivo a pensarci :) Devi solo gestire il caso in cui non esiste una coppia assoluta (voci dispari)
Saurav Kumar

1

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)

1

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]]

1

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)]

0

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)

0

Il titolo di questa domanda è fuorviante, sembra che tu stia cercando coppie consecutive, ma se vuoi iterare sull'insieme di tutte le coppie possibili di quanto funzionerà:

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

0

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

0

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)


0
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)
> 

puoi scegliere qualsiasi riempitivo per None
Vishesh Mangla

-1

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.


-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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.