1291 Stimmen

Wie kann man durch zwei Listen parallel iterieren?

Ich habe zwei Iterables in Python, und ich möchte sie paarweise durchgehen:

foo = (1, 2, 3)
bar = (4, 5, 6)

for (f, b) in some_iterator(foo, bar):
    print("f: ", f, "; b: ", b)

Das Ergebnis sollte sein:

f: 1; b: 4
f: 2; b: 5
f: 3; b: 6

Eine ist es, über die Indizes zu iterieren:

for i in range(len(foo)):
    print("f: ", foo[i], "; b: ", bar[i])

Aber das erscheint mir etwas unpythonisch. Gibt es einen besseren Weg, dies zu tun?

1999voto

unutbu Punkte 769083

Python 3

for f, b in zip(foo, bar):
    print(f, b)

zip stoppt, wenn der kürzere der beiden foo o bar stoppt.

Sur Python 3 , zip gibt einen Iterator von Tupeln zurück, wie itertools.izip in Python2. Um eine Liste von Tupeln zu erhalten von Tupeln zu erhalten, verwenden Sie list(zip(foo, bar)) . Und um zu zippen, bis beide Iteratoren erschöpft sind erschöpft sind, würden Sie verwenden itertools.zip_longest .

Python 2

Sur Python 2 , zip gibt eine Liste von Tupeln zurück. Dies ist in Ordnung, wenn foo y bar sind nicht massiv. Wenn sie beide massiv sind, dann bilden zip(foo,bar) ist eine unnötig massive temporäre Variable und sollte ersetzt werden durch itertools.izip oder itertools.izip_longest die anstelle einer Liste einen Iterator zurückgibt.

import itertools
for f,b in itertools.izip(foo,bar):
    print(f,b)
for f,b in itertools.izip_longest(foo,bar):
    print(f,b)

izip stoppt, wenn entweder foo o bar erschöpft ist. izip_longest stoppt, wenn beide foo y bar erschöpft sind. Wenn der/die kürzere(n) Iterator(en) erschöpft ist/sind, izip_longest ergibt ein Tupel mit None an der Position, die diesem Iterator entspricht. Sie können auch einen anderen fillvalue neben None wenn Sie es wünschen. Siehe hier für die ganze Geschichte .


Beachten Sie auch, dass zip und seine zip -ähnliche Brethen können eine beliebige Anzahl von Iterablen als Argumente akzeptieren. Zum Beispiel,

for num, cheese, color in zip([1,2,3], ['manchego', 'stilton', 'brie'], 
                              ['red', 'blue', 'green']):
    print('{} {} {}'.format(num, color, cheese))

druckt

1 red manchego
2 blue stilton
3 green brie

102voto

Karl Guertin Punkte 4206

Sie wollen die zip Funktion.

for (f,b) in zip(foo, bar):
    print "f: ", f ,"; b: ", b

27voto

Sun Bear Punkte 6448

Aufbauend auf der Antwort von @unutbu Ich habe die Iterationsleistung von zwei identischen Listen verglichen, wenn ich die Python 3.6's zip() Funktionen, Pythons enumerate() Funktion mit Hilfe eines manuellen Zählers (siehe count() Funktion), unter Verwendung einer Indexliste, und während eines speziellen Szenarios, in dem die Elemente einer der beiden Listen (entweder foo o bar ) kann zum Indexieren der anderen Liste verwendet werden. Ihre Leistung beim Drucken bzw. Erstellen einer neuen Liste wurde mit Hilfe der timeit() Funktion, wobei die Anzahl der Wiederholungen 1000-mal verwendet wurde. Eines der Python-Skripte, die ich zur Durchführung dieser Untersuchungen erstellt hatte, ist unten aufgeführt. Die Größen der foo y bar Die Listen reichten von 10 bis 1.000.000 Elementen.

Ergebnisse:

  1. Für Druckzwecke: Es wurde festgestellt, dass die Leistungen aller betrachteten Ansätze in etwa mit denen der zip() Funktion nach Berücksichtigung einer Genauigkeitstoleranz von +/-5%. Eine Ausnahme trat auf, wenn die Listengröße kleiner als 100 Elemente war. In einem solchen Szenario war die Index-Listen-Methode etwas langsamer als die zip() Funktion, während die enumerate() Funktion war ~9% schneller. Die anderen Methoden lieferten eine ähnliche Leistung wie die zip() Funktion.

    Print loop 1000 reps

  2. Zum Erstellen von Listen: Es wurden zwei Arten der Listenerstellung untersucht: mit Hilfe der (a) list.append() Methode und (b) Listenverstehen . Nach Berücksichtigung einer Genauigkeitstoleranz von +/-5 % ergibt sich für diese beiden Ansätze die zip() Funktion war schneller als die enumerate() Funktion, als die Verwendung eines Listenindexes, als die Verwendung eines manuellen Zählers. Der Leistungsgewinn durch die zip() Funktion bei diesen Vergleichen 5 % bis 60 % schneller sein kann. Interessanterweise ist die Verwendung des Elements von foo zum Index bar können gleichwertige oder schnellere Leistungen (5 % bis 20 %) erbringen als die zip() Funktion.

    Creating List - 1000reps

Diese Ergebnisse sind sinnvoll:

Ein Programmierer muss die Menge an Rechenzeit pro Operation bestimmen, die sinnvoll ist oder die von Bedeutung ist.

Wenn dieses Zeitkriterium beispielsweise für Druckzwecke 1 Sekunde beträgt, d. h. 10**0 Sekunden, dann sehen wir, wenn wir die y-Achse des Diagramms links bei 1 Sekunde betrachten und sie horizontal projizieren, bis sie die Monomialkurven erreicht, dass Listengrößen von mehr als 144 Elementen für den Programmierer erhebliche Rechenkosten und Bedeutung haben. Das heißt, dass jeder Leistungsgewinn durch die in dieser Untersuchung erwähnten Ansätze für kleinere Listengrößen für den Programmierer unbedeutend sein wird. Der Programmierer wird zu dem Schluss kommen, dass die Leistung der zip() Funktion zur Iteration von Druckanweisungen ist ähnlich wie die anderen Ansätze.

Schlussfolgerung

Eine beachtliche Leistung kann durch die Verwendung der zip() Funktion, um parallel durch zwei Listen zu iterieren, während list Erstellung. Bei der parallelen Iteration durch zwei Listen, um die Elemente der beiden Listen auszudrucken, wird die zip() Funktion wird eine ähnliche Leistung wie die enumerate() Funktion, zur Verwendung einer manuellen Zählervariablen, zur Verwendung einer Indexliste und zum speziellen Szenario, bei dem die Elemente einer der beiden Listen (entweder foo o bar ) kann zum Indexieren der anderen Liste verwendet werden.

Das Python3.6-Skript, das zur Untersuchung der Listenerstellung verwendet wurde.

import timeit
import matplotlib.pyplot as plt
import numpy as np

def test_zip( foo, bar ):
    store = []
    for f, b in zip(foo, bar):
        #print(f, b)
        store.append( (f, b) ) 

def test_enumerate( foo, bar ):
    store = []
    for n, f in enumerate( foo ):
        #print(f, bar[n])
        store.append( (f, bar[n]) ) 

def test_count( foo, bar ):
    store = []
    count = 0
    for f in foo:
        #print(f, bar[count])
        store.append( (f, bar[count]) )
        count += 1

def test_indices( foo, bar, indices ):
    store = []
    for i in indices:
        #print(foo[i], bar[i])
        store.append( (foo[i], bar[i]) )

def test_existing_list_indices( foo, bar ):
    store = []
    for f in foo:
        #print(f, bar[f])
        store.append( (f, bar[f]) )

list_sizes = [ 10, 100, 1000, 10000, 100000, 1000000 ]
tz = []
te = []
tc = []
ti = []
tii= []

tcz = []
tce = []
tci = []
tcii= []

for a in list_sizes:
    foo = [ i for i in range(a) ]
    bar = [ i for i in range(a) ]
    indices = [ i for i in range(a) ]
    reps = 1000

    tz.append( timeit.timeit( 'test_zip( foo, bar )',
                              'from __main__ import test_zip, foo, bar',
                              number=reps
                              )
               )
    te.append( timeit.timeit( 'test_enumerate( foo, bar )',
                              'from __main__ import test_enumerate, foo, bar',
                              number=reps
                              )
               )
    tc.append( timeit.timeit( 'test_count( foo, bar )',
                              'from __main__ import test_count, foo, bar',
                              number=reps
                              )
               )
    ti.append( timeit.timeit( 'test_indices( foo, bar, indices )',
                              'from __main__ import test_indices, foo, bar, indices',
                              number=reps
                              )
               )
    tii.append( timeit.timeit( 'test_existing_list_indices( foo, bar )',
                               'from __main__ import test_existing_list_indices, foo, bar',
                               number=reps
                               )
                )

    tcz.append( timeit.timeit( '[(f, b) for f, b in zip(foo, bar)]',
                               'from __main__ import foo, bar',
                               number=reps
                               )
                )
    tce.append( timeit.timeit( '[(f, bar[n]) for n, f in enumerate( foo )]',
                               'from __main__ import foo, bar',
                               number=reps
                               )
                )
    tci.append( timeit.timeit( '[(foo[i], bar[i]) for i in indices ]',
                               'from __main__ import foo, bar, indices',
                               number=reps
                               )
                )
    tcii.append( timeit.timeit( '[(f, bar[f]) for f in foo ]',
                                'from __main__ import foo, bar',
                                number=reps
                                )
                 )

print( f'te  = {te}' )
print( f'ti  = {ti}' )
print( f'tii = {tii}' )
print( f'tc  = {tc}' )
print( f'tz  = {tz}' )

print( f'tce  = {te}' )
print( f'tci  = {ti}' )
print( f'tcii = {tii}' )
print( f'tcz  = {tz}' )

fig, ax = plt.subplots( 2, 2 )
ax[0,0].plot( list_sizes, te, label='enumerate()', marker='.' )
ax[0,0].plot( list_sizes, ti, label='index-list', marker='.' )
ax[0,0].plot( list_sizes, tii, label='element of foo', marker='.' )
ax[0,0].plot( list_sizes, tc, label='count()', marker='.' )
ax[0,0].plot( list_sizes, tz, label='zip()', marker='.')
ax[0,0].set_xscale('log')
ax[0,0].set_yscale('log')
ax[0,0].set_xlabel('List Size')
ax[0,0].set_ylabel('Time (s)')
ax[0,0].legend()
ax[0,0].grid( b=True, which='major', axis='both')
ax[0,0].grid( b=True, which='minor', axis='both')

ax[0,1].plot( list_sizes, np.array(te)/np.array(tz), label='enumerate()', marker='.' )
ax[0,1].plot( list_sizes, np.array(ti)/np.array(tz), label='index-list', marker='.' )
ax[0,1].plot( list_sizes, np.array(tii)/np.array(tz), label='element of foo', marker='.' )
ax[0,1].plot( list_sizes, np.array(tc)/np.array(tz), label='count()', marker='.' )
ax[0,1].set_xscale('log')
ax[0,1].set_xlabel('List Size')
ax[0,1].set_ylabel('Performances ( vs zip() function )')
ax[0,1].legend()
ax[0,1].grid( b=True, which='major', axis='both')
ax[0,1].grid( b=True, which='minor', axis='both')

ax[1,0].plot( list_sizes, tce, label='list comprehension using enumerate()',  marker='.')
ax[1,0].plot( list_sizes, tci, label='list comprehension using index-list()',  marker='.')
ax[1,0].plot( list_sizes, tcii, label='list comprehension using element of foo',  marker='.')
ax[1,0].plot( list_sizes, tcz, label='list comprehension using zip()',  marker='.')
ax[1,0].set_xscale('log')
ax[1,0].set_yscale('log')
ax[1,0].set_xlabel('List Size')
ax[1,0].set_ylabel('Time (s)')
ax[1,0].legend()
ax[1,0].grid( b=True, which='major', axis='both')
ax[1,0].grid( b=True, which='minor', axis='both')

ax[1,1].plot( list_sizes, np.array(tce)/np.array(tcz), label='enumerate()', marker='.' )
ax[1,1].plot( list_sizes, np.array(tci)/np.array(tcz), label='index-list', marker='.' )
ax[1,1].plot( list_sizes, np.array(tcii)/np.array(tcz), label='element of foo', marker='.' )
ax[1,1].set_xscale('log')
ax[1,1].set_xlabel('List Size')
ax[1,1].set_ylabel('Performances ( vs zip() function )')
ax[1,1].legend()
ax[1,1].grid( b=True, which='major', axis='both')
ax[1,1].grid( b=True, which='minor', axis='both')

plt.show()

23voto

Vlad Bezden Punkte 71128

Sie sollten ' zip ' Funktion. Hier ist ein Beispiel, wie Ihre eigene Zip-Funktion aussehen kann

def custom_zip(seq1, seq2):
    it1 = iter(seq1)
    it2 = iter(seq2)
    while True:
        yield next(it1), next(it2)

6voto

Chad Punkte 1215

So geht's mit dem Listenverständnis:

a = (1, 2, 3)
b = (4, 5, 6)
[print('f:', i, '; b', j) for i, j in zip(a, b)]

Drucke:

f: 1 ; b 4
f: 2 ; b 5
f: 3 ; b 6

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X