Wie erstelle ich eine for
Schleife 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
Wie erstelle ich eine for
Schleife 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
Antworten:
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 izip
die 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 pairwise
Rezept in Pythons eigener itertools
Dokumentation 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)
s -> (s0,s1), (s1,s2), (s2, s3), ...
itertools
Rezeptfunktion. Natürlich ist deine schneller ...
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.
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.for i, j, k in zip(data[0::3], data[1::3], data[2::3]):
import
gehört nicht dazu.
>>> 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']
zip
gibt zip
in Python 3 ein Objekt zurück, das nicht tiefgestellt werden kann. Es muss zu einer Sequenz umgewandelt werden ( list
, tuple
usw.) die ersten, aber „nicht funktioniert“ ist ein bisschen weit hergeholt.
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])
((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.
Obwohl alle Antworten zip
korrekt 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 it
es sich tatsächlich um einen Iterator handelt, nicht nur um einen iterierbaren. Wenn es sich it
bereits 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)
it
es 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.
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)]
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)
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 grouper
voniteration_utilities
wahrscheinlich etwas schneller.
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.grouper
erlaubt 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 grouper
Sie 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)]
Verwenden Sie das zip
unditer
Befehle zusammen:
Ich finde diese Lösung iter
ziemlich 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 N
Elemente zu einem Zeitpunkt:
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)
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.
[number::number]
Syntax. hilfreich für diejenigen, die Python nicht oft verwenden
Mit dem Auspacken:
l = [1,2,3,4,5,6]
while l:
i, k, *l = l
print(str(i), '+', str(k), '=', str(i+k))
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)
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}')
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]]
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)]
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)
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
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.)
Hier können wir eine alt_elem
Methode 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.