Python: Wie hängt man zwei Listen aneinander?
Es gibt fünf vernünftige Arten, zwei Listen zu verbinden. Sie unterscheiden sich darin, ob du eine neue Liste bekommst oder eine bestehende veränderst — und dieser Unterschied ist für anderen Code sichtbar, der dieselbe Liste hält.
Die Möglichkeiten
a = [1, 2]
b = [3, 4]
a + b : [1, 2, 3, 4] neue Liste
[*a, *b] : [1, 2, 3, 4] neue Liste
list(chain(a, b)) : [1, 2, 3, 4] neue Liste
x.extend(b) : [1, 2, 3, 4] veraendert x
y += b : [1, 2, 3, 4] veraendert y
Die ersten drei lassen beide Eingaben in Ruhe. Die letzten beiden verändern die linke Liste.
Welche du willst
a + b, wenn du eine dritte Liste willst und die Originale so bleiben
sollen wie sie sind. Die klarste Variante und die naheliegende Wahl.
[*a, *b] (PEP 448), wenn du mehr als zwei Dinge verbindest oder Typen mischst. Es
nimmt beliebige iterierbare Objekte und liest sich bei drei oder vier gut.
a.extend(b), wenn du an eine Liste anhängst, die dir gehört, und keine
Kopie brauchst. Am günstigsten, weil nichts neu gebaut wird.
chain(a, b), wenn du nur iterieren willst. Es baut überhaupt nichts —
nützlich, wenn die Listen groß sind und das Ergebnis direkt in eine Schleife
geht.
+ und += sind nicht dasselbe
Das ist der Teil, den man kennen sollte. Mit id() nachgewiesen:
p = p + [3] : new object? True
q += [3] : new object? False <- same object, mutated
r.extend([3]) : new object? False
+= ist bei einer Liste keine Kurzschreibweise für x = x + y. Es ruft
__iadd__ auf, das an Ort und Stelle erweitert und dasselbe Objekt
zurückgibt.
Und das zählt, sobald zwei Namen auf dieselbe Liste zeigen:
after m += [3] : m=[1, 2, 3] alias=[1, 2, 3] <- alias saw it
after m2 = m2 + [3] : m2=[1, 2, 3] alias2=[1, 2] <- alias did NOT
Zwei Zeilen, die gleichwertig aussehen — und der zweite Name (die Variable
eines Aufrufers, ein Eintrag in einem Dictionary, ein Attribut an einem Objekt)
sieht die eine Änderung und die andere nicht. Wenn die Liste als
Funktionsparameter hereinkam, verändert += sie für den Aufrufer.
append ist nicht extend
Das andere, was gerne verwechselt wird:
[1,2].append([3,4]) : [1, 2, [3, 4]] <- one new element, a list
[1,2].extend([3,4]) : [1, 2, 3, 4]
append fügt ein
Element hinzu, was auch immer es ist. extend fügt jedes Element eines
iterierbaren Objekts hinzu.
extend ist großzügig, + ist streng
[1,2] + (3,4) -> TypeError: can only concatenate list (not "tuple") to list
+ besteht darauf, dass beide Seiten Listen sind. extend nimmt alles
Iterierbare:
extend with tuple, str, generator : [1, 2, 3, 4, 'a', 'b', 5]
Sieh dir an, was mit dem String passiert ist. extend("ab") hat 'a' und
'b' als zwei getrennte Elemente hinzugefügt, denn ein String ist eine Folge
von Zeichen. Wenn du einen String als Ganzes anfügen wolltest, ist das
append. Das ist dieselbe Art von Überraschung wie beim rekursiven Flachmachen
im Artikel darüber.
[*a, *b] nimmt ebenfalls beliebige iterierbare Objekte, ohne dass das
String-Problem versteckt wäre — man sieht das Entpacken.
Verwende + nicht in einer Schleife
ergebnis = []
for teil in teile:
ergebnis = ergebnis + teil # baut jedes Mal alles neu
2000 sublists, 3 runs each:
out = out + p : 0.0071 s
out.extend(p) : 0.0001 s
chain.from_iterable : 0.0001 s
'+' is 68x extend
Jedes + legt eine neue Liste an und kopiert alles bisher Angesammelte, die
Schleife ist damit quadratisch. extend hängt an die bestehende an.
Um eine Liste von Listen zu verbinden, ist chain.from_iterable der direkte
Ausdruck dessen, was du willst — der Artikel über das Flachmachen geht näher
darauf ein.
Die Falle beim Vervielfachen
Verwandt und eine Erwähnung wert, weil es in denselben Zusammenhängen aufkommt:
[[0]] * 3 : [[0], [0], [0]]
after rows[0].append(9) : [[0, 9], [0, 9], [0, 9]]
* vervielfacht bei einer Liste die Referenz, alle drei Zeilen sind also
dieselbe Liste. Ein append über eine davon ändert alle.
[[0] for _ in range(3)] : [[0, 9], [0], [0]]
Die Comprehension wertet [0] in jedem Durchlauf neu aus und liefert drei
getrennte Listen. Für ein Gitter oder eine Reihe von Behältern ist das das
Gewollte.
Noch ein Unterschied
Aneinanderhängen erhält Reihenfolge und Dubletten, Mengen nicht:
[1,2] + [2,1] : [1, 2, 2, 1]
set union for comparison : [1, 2]
Wenn du zu set(a) | set(b) greifst, um zwei Listen zu verbinden, vergewissere
dich, dass du das Entfernen der Dubletten wirklich willst und auf die
Reihenfolge verzichten kannst.
Hinweis zu Netcup (Werbung)
Der deutsche Hoster Netcup bietet unter anderem günstige und zugleich leistungsstarke Webhosting Pakete, KVM-basierte Root Server und dezidierte Server an. Mit unseren Gutscheincodes kannst du noch mehr Geld sparen (6€ bei deiner ersten Bestellung, 30% Rabatt auf alle KVM-basierten Root Server, ...).