Wie kann man aus einer Liste von Listen eine flache Liste machen?

Ich frage mich, ob es eine Abkürzung gibt, um eine einfache Liste aus einer Liste von Listen in Python zu erstellen.

Ich kann das in einer "for"-Schleife machen, aber vielleicht gibt es einen coolen "Einzeiler"? Ich habe es mit reduce() versucht, aber ich bekomme einen Fehler.

Code

l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
reduce(lambda x, y: x.extend(y), l)

Fehlermeldung

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <lambda>
AttributeError: 'NoneType' object has no attribute 'extend'
Lösung

Gegeben eine Liste von Listen l,

flat_list = [item for sublist in l for item in sublist]`

was bedeutet:

flat_list = []
for sublist in l:
    for item in sublist:
        flat_list.append(item)

ist schneller als die bisher veröffentlichten Abkürzungen. (l ist die zu glättende Liste.)

Hier ist die entsprechende Funktion:

flatten = lambda l: [item for sublist in l for item in sublist]

Als Beweis können Sie das Modul timeit in der Standardbibliothek verwenden:

$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' '[item for sublist in l for item in sublist]'
10000 loops, best of 3: 143 usec per loop
$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'sum(l, [])'
1000 loops, best of 3: 969 usec per loop
$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'reduce(lambda x,y: x+y,l)'
1000 loops, best of 3: 1.1 msec per loop

Erklärung: die auf + basierenden Abkürzungen (einschließlich der impliziten Verwendung in sum) sind notwendigerweise O(L**2), wenn es L Unterlisten gibt -- da die Zwischenergebnisliste immer länger wird, wird bei jedem Schritt ein neues Zwischenergebnislisten-Objekt zugewiesen, und alle Elemente des vorherigen Zwischenergebnisses müssen rüberkopiert werden (sowie einige neue am Ende hinzugefügt werden). Der Einfachheit halber und ohne Verlust der Allgemeingültigkeit sei angenommen, dass Sie L Unterlisten mit jeweils I Elementen haben: die ersten I Elemente werden L-1 mal hin- und herkopiert, die zweiten I Elemente L-2 mal, und so weiter; die Gesamtzahl der Kopien ist I mal die Summe von x für x von 1 bis L ausgeschlossen, d.h. I * (L**2)/2.

Das Listenverständnis erzeugt nur einmal eine Liste und kopiert jedes Element (von seinem ursprünglichen Aufenthaltsort in die Ergebnisliste) ebenfalls genau einmal.

Kommentare (29)

Anmerkung des Autors: Das ist ineffizient. Aber es macht Spaß, weil monoids großartig ist. Es ist nicht geeignet für die Produktion Python-Code.

>>> sum(l, [])
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Dies summiert einfach die Elemente der im ersten Argument übergebenen iterable, wobei das zweite Argument als Anfangswert der Summe behandelt wird (wenn es nicht angegeben wird, wird stattdessen 0 verwendet und in diesem Fall wird ein Fehler ausgegeben).

Da Sie verschachtelte Listen summieren, erhalten Sie tatsächlich [1,3]+[2,4] als Ergebnis von sum([[1,3],[2,4]],[]), was gleich [1,3,2,4] ist.

Beachten Sie, dass dies nur bei Listen von Listen funktioniert. Für Listen von Listen von Listen brauchen Sie eine andere Lösung.

Kommentare (15)
from functools import reduce #python 3

>>> l = [[1,2,3],[4,5,6], [7], [8,9]]
>>> reduce(lambda x,y: x+y,l)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Die Methode extend() in Ihrem Beispiel verändert x, anstatt einen nützlichen Wert zurückzugeben (den reduce() erwartet).

Ein schnellerer Weg, die reduce Version zu machen, wäre

>>> import operator
>>> l = [[1,2,3],[4,5,6], [7], [8,9]]
>>> reduce(operator.concat, l)
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Kommentare (6)