Durchlaufen von jeweils zwei Elementen in einer Liste

208

Wie erstelle ich eine forSchleife oder ein Listenverständnis, sodass mir jede Iteration zwei Elemente gibt?

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

for i,k in ???:
    print str(i), '+', str(k), '=', str(i+k)

Ausgabe:

1+2=3
3+4=7
5+6=11
Jackhab
quelle
2
Für überlappende Paare: stackoverflow.com/questions/5434891/…
user202729

Antworten:

231

Sie benötigen eine pairwise()(oder grouped()) Implementierung.

Für 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)

Oder allgemeiner:

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 können Sie durch izipdie integrierte zip()Funktion ersetzen und die löschen import.

Alle Kredite zu martineau für seine Antwort auf meine Frage , ich habe diese sehr effizient erwiesen , da sie nur einmal über die Liste iteriert und erstellen Sie keine unnötigen Listen in dem Prozess.

NB : Dies sollte nicht mit dem pairwiseRezept in Pythons eigener itertoolsDokumentation verwechselt werden , das ergibt s -> (s0, s1), (s1, s2), (s2, s3), ..., wie @lazyr in den Kommentaren hervorhebt .

Kleine Ergänzung für diejenigen, die mit mypy auf Python 3 eine Typprüfung durchführen möchten :

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)
Johnsyweb
quelle
16
Nicht zu verwechseln mit der paarweisen Funktion, die im Abschnitt mit den Rezepten für itertools vorgeschlagen wurde und ergibts -> (s0,s1), (s1,s2), (s2, s3), ...
Lauritz V. Thaulow
1
Es macht eine andere Sache. Ihre Version liefert nur die Hälfte der Anzahl der Paare im Vergleich zur gleichnamigen itertoolsRezeptfunktion. Natürlich ist deine schneller ...
Sven Marnach
Huh? Ihre Funktion und die Funktion, auf die ich mich bezog, machen verschiedene Dinge, und das war der Punkt meines Kommentars.
Lauritz V. Thaulow
5
ACHTUNG! Wenn Sie diese Funktionen verwenden, besteht das Risiko, dass Sie nicht über die letzten Elemente einer Iterable iterieren. Beispiel: Liste (gruppiert ([1,2,3], 2)) >>> [(1, 2)] .. wenn Sie [(1,2), (3,)] erwarten würden
egafni
4
@ Erik49: In dem in der Frage angegebenen Fall wäre es nicht sinnvoll, ein "unvollständiges" Tupel zu haben. Wenn Sie ein unvollständiges Tupel einfügen möchten, können Sie izip_longest()stattdessen anstelle von verwenden izip(). ZB: list(izip_longest(*[iter([1, 2, 3])]*2, fillvalue=0))-> [(1, 2), (3, 0)]. Hoffe das hilft.
Johnsyweb
191

Nun, Sie brauchen ein Tupel mit 2 Elementen

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)

Wo:

  • data[0::2] bedeutet, eine Teilmengen-Sammlung von Elementen zu erstellen, die (index % 2 == 0)
  • zip(x,y) Erstellt eine Tupelsammlung aus x- und y-Sammlungen derselben Indexelemente.
Margus
quelle
8
Dies kann auch erweitert werden, wenn mehr als zwei Elemente erforderlich sind. Zum Beispielfor i, j, k in zip(data[0::3], data[1::3], data[2::3]):
Lebensbalance
19
So viel sauberer als einen Import einzuziehen und eine Funktion zu definieren!
kmarsh
7
@kmarsh: Aber das funktioniert nur bei Sequenzen, die Funktion funktioniert bei jeder iterierbaren; und dies verwendet O (N) zusätzlichen Platz, die Funktion nicht; Andererseits ist dies im Allgemeinen schneller. Es gibt gute Gründe, sich für den einen oder anderen zu entscheiden. Angst zu haben importgehört nicht dazu.
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']
Pyanon
quelle
1
Dies funktioniert nicht auf Python-3.6.0, aber immer noch auf Python-2.7.10
Hamid Rohani
6
@HamidRohani zipgibt zipin Python 3 ein Objekt zurück, das nicht tiefgestellt werden kann. Es muss zu einer Sequenz umgewandelt werden ( list, tupleusw.) die ersten, aber „nicht funktioniert“ ist ein bisschen weit hergeholt.
Vaultah
58

Eine einfache Lösung.

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

für i im Bereich (0, len (l), 2):
    drucke str (l [i]), '+', str (l [i + 1]), '=', str (l [i] + l [i + 1])
taskinoor
quelle
1
Was ist, wenn Ihre Liste nicht gerade ist und Sie nur die letzte Nummer so anzeigen möchten, wie sie ist?
Hans de Jong
@ HansdeJong hat dich nicht verstanden. Bitte erklären Sie etwas mehr.
Aufgabe
2
Vielen Dank. Ich habe schon herausgefunden, wie es geht. Das Problem war, wenn Sie eine Liste hatten, die nicht einmal eine Anzahl von Zahlen enthielt, wurde ein Indexfehler angezeigt. Es wurde mit einem Versuch gelöst: außer:
Hans de Jong
Oder ((l[i], l[i+1])for i in range(0, len(l), 2))für einen Generator, kann leicht für längere Tupel modifiziert werden.
Basel Shishani
44

Obwohl alle Antworten zipkorrekt sind, finde ich, dass die Implementierung der Funktionalität selbst zu besser lesbarem Code führt:

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

Der it = iter(it)Teil stellt sicher, dass ites sich tatsächlich um einen Iterator handelt, nicht nur um einen iterierbaren. Wenn es sich itbereits um einen Iterator handelt, ist diese Zeile ein No-Op.

Verwendung:

for a, b in pairwise([0, 1, 2, 3, 4, 5]):
    print(a + b)
Mäuse
quelle
2
Diese Lösung ermöglicht die Verallgemeinerung auf die Größe von Tupeln> 2
Guilloptero
1
Diese Lösung funktioniert auch, wenn ites sich nur um einen Iterator und nicht um einen iterierbaren handelt. Die anderen Lösungen scheinen auf der Möglichkeit zu beruhen, zwei unabhängige Iteratoren für die Sequenz zu erstellen.
Skyking
Ich habe diesen Ansatz unter stackoverflow.com/a/16815056/2480481 gefunden, bevor ich diese Antwort gesehen habe. Ist sauberer, einfacher als mit zip () umzugehen.
m3nda
2
Ich mag es, dass dadurch vermieden werden kann, dass die Speichernutzung als akzeptierte Antwort verdreifacht wird.
Kentzo
Dies funktioniert nicht gut mit forSchleifen in Python 3.5+ aufgrund von PEP 479 , das alle StopIterationin einem Generator ausgelösten durch a ersetzt RuntimeError.
Sidney
27

Ich hoffe, dies wird noch eleganter.

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

[(1, 2), (3, 4), (5, 6)]
Vivek Srinivasan
quelle
18

Falls Sie an der Leistung interessiert sind, habe ich einen kleinen Benchmark (unter Verwendung meiner Bibliothek simple_benchmark) durchgeführt, um die Leistung der Lösungen zu vergleichen, und eine Funktion aus einem meiner Pakete hinzugefügt: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)

Geben Sie hier die Bildbeschreibung ein

Wenn Sie also die schnellste Lösung ohne externe Abhängigkeiten wünschen, sollten Sie wahrscheinlich nur den von Johnysweb angegebenen Ansatz verwenden (zum Zeitpunkt des Schreibens ist dies die am besten bewertete und akzeptierte Antwort).

Wenn Ihnen die zusätzliche Abhängigkeit nichts ausmacht, dann die groupervoniteration_utilities wahrscheinlich etwas schneller.

Zusätzliche Gedanken

Einige der Ansätze weisen einige Einschränkungen auf, die hier nicht erörtert wurden.

Beispielsweise funktionieren einige Lösungen nur für Sequenzen (dh Listen, Zeichenfolgen usw.), z. B. Margus / Pyanon / Taskinoor-Lösungen, die die Indizierung verwenden, während andere Lösungen für alle iterierbaren Lösungen (dh Sequenzen und) arbeiten dh Generatoren, Iteratoren) wie Johnysweb / funktionieren. mic_e / meine Lösungen.

Dann stellte Johnysweb auch eine Lösung zur Verfügung, die für andere Größen als 2 funktioniert, während die anderen Antworten dies nicht tun (okay, das iteration_utilities.groupererlaubt auch das Einstellen der Anzahl der Elemente auf "Gruppieren").

Dann stellt sich auch die Frage, was passieren soll, wenn die Liste eine ungerade Anzahl von Elementen enthält. Sollte der verbleibende Gegenstand abgewiesen werden? Sollte die Liste aufgefüllt werden, um eine gleichmäßige Größe zu erzielen? Sollte der verbleibende Artikel einzeln zurückgegeben werden? Die andere Antwort spricht diesen Punkt nicht direkt an. Wenn ich jedoch nichts übersehen habe, folgen sie alle dem Ansatz, dass das verbleibende Element verworfen werden sollte (mit Ausnahme der Antwort der Taskinoors - dies führt tatsächlich zu einer Ausnahme).

Mit können grouperSie entscheiden, was Sie tun möchten:

>>> 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)]
MSeifert
quelle
12

Verwenden Sie das zipunditer Befehle zusammen:

Ich finde diese Lösung iterziemlich elegant:

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

Was ich in der Python 3-Zip-Dokumentation gefunden habe .

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

So verallgemeinern Sie auf NElemente zu einem Zeitpunkt:

N = 2
list(zip(*([iter(l)] * N)))
# [(1, 2), (3, 4), (5, 6)]
Quantenmechaniker
quelle
10
for (i, k) in zip(l[::2], l[1::2]):
    print i, "+", k, "=", i+k

zip(*iterable) Gibt ein Tupel mit dem nächsten Element jeder Iterable zurück.

l[::2] Gibt das erste, dritte, fünfte usw. Element der Liste zurück: Der erste Doppelpunkt zeigt an, dass das Slice am Anfang beginnt, da keine Nummer dahinter steht. Der zweite Doppelpunkt wird nur benötigt, wenn Sie einen Schritt im Slice möchten '(in diesem Fall 2).

l[1::2]macht dasselbe, beginnt aber im zweiten Element der Listen, sodass das 2., 4., 6. usw. Element der ursprünglichen Liste zurückgegeben wird.

alexpinho98
quelle
4
Diese Antwort wurde bereits vor zwei Jahren von Margus gegeben. stackoverflow.com/questions/5389507/…
cababunga
1
1 zur Erläuterung der Funktionsweise der [number::number]Syntax. hilfreich für diejenigen, die Python nicht oft verwenden
Alby
2

Mit dem Auspacken:

l = [1,2,3,4,5,6]
while l:
    i, k, *l = l
    print(str(i), '+', str(k), '=', str(i+k))
toaruScar
quelle
Beeindruckend! Warum ich nicht darüber nachdenken konnte :) Sie müssen nur den Fall behandeln, in dem es kein absolutes Paar gibt (ungerade Einträge)
Saurav Kumar
1

Für jeden, der es helfen könnte, ist hier eine Lösung für ein ähnliches Problem, jedoch mit überlappenden Paaren (anstelle von sich gegenseitig ausschließenden Paaren).

Aus der Python itertools-Dokumentation :

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)

Oder allgemeiner:

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)
Chris Malek
quelle
1

Sie können das Paket more_itertools verwenden .

import more_itertools

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

Ich muss eine Liste durch eine Zahl teilen und so fixieren.

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]]
Ali Katkar
quelle
1

Dafür gibt es viele Möglichkeiten. Beispielsweise:

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)]
Binit Bhagat
quelle
0

Ich dachte, dass dies ein guter Ort ist, um meine Verallgemeinerung für n> 2 zu teilen, was nur ein Schiebefenster über eine iterable ist:

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

    return itertools.izip(*its)
Yuval
quelle
0

Der Titel dieser Frage ist irreführend. Sie scheinen nach aufeinanderfolgenden Paaren zu suchen. Wenn Sie jedoch die Menge aller möglichen Paare durchlaufen möchten, funktioniert dies wie folgt:

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

Mithilfe der Eingabe können Sie Daten mit dem statischen Analysetool mypy überprüfen :

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
Vlad Bezden
quelle
0

Ein vereinfachter Ansatz:

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

Dies ist nützlich, wenn Ihr Array ein Array ist und Sie es paarweise durchlaufen möchten. Um auf Drillingen oder mehr zu iterieren, ändern Sie einfach den Schrittbefehl "Bereich", zum Beispiel:

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

(Sie müssen mit überschüssigen Werten umgehen, wenn Ihre Array-Länge und der Schritt nicht passen.)

mchrgr2000
quelle
-1

Hier können wir eine alt_elemMethode haben , die in Ihre for-Schleife passt.

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)

Ausgabe:

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)

Hinweis: Die obige Lösung ist möglicherweise nicht effizient, wenn man die in func ausgeführten Vorgänge berücksichtigt.

Sanket Sudake
quelle
-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)
user391
quelle