Wie erstelle ich eine flache Liste aus einer Liste von Listen?

3371

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

Ich kann das in einer forSchleife machen, aber vielleicht gibt es einen coolen "Einzeiler"? Ich habe es mit versucht reduce(), aber ich bekomme eine Fehlermeldung.

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'
Emma
quelle
20
Hier wird dies ausführlich besprochen : rightfootin.blogspot.com/2006/09/more-on-python-flatten.html , wobei verschiedene Methoden zum Reduzieren beliebig verschachtelter Listen von Listen erörtert werden. Eine interessante Lektüre!
RichieHindle
6
Einige andere Antworten sind besser, aber der Grund, warum Ihre fehlschlagen, ist, dass die Methode 'verlängern' immer Keine zurückgibt. Bei einer Liste mit der Länge 2 funktioniert dies, es wird jedoch None zurückgegeben. Für eine längere Liste werden die ersten 2 Argumente verbraucht, die None zurückgeben. Es geht dann weiter mit None.extend (<drittes Argument>), was diesen Fehler verursacht
11.
Die @ shawn-chin-Lösung ist hier die pythonischere. Wenn Sie jedoch den Sequenztyp beibehalten müssen, z. B. ein Tupel Tupel anstelle einer Liste von Listen, sollten Sie reduzieren (operator.concat, tuple_of_tuples) verwenden. Die Verwendung von operator.concat mit Tupeln scheint schneller zu funktionieren als chain.from_iterables mit list.
Meitham

Antworten:

4791

Angesichts einer 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 Verknüpfungen. ( lIst die Liste zu reduzieren.)

Hier ist die entsprechende Funktion:

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

Als Beweis können Sie das timeitModul 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

Erläuterung: Die Verknüpfungen, die auf +(einschließlich der impliziten Verwendung in sum) basieren, sind notwendigerweise erforderlich, O(L**2)wenn L Unterlisten vorhanden sind. Da die Zwischenergebnisliste immer länger wird, wird bei jedem Schritt ein neues Zwischenergebnislistenobjekt und alle Elemente zugewiesen im vorherigen Zwischenergebnis muss kopiert werden (sowie einige neue, die am Ende hinzugefügt wurden). Nehmen wir zur Vereinfachung und ohne tatsächlichen Verlust der Allgemeinheit an, Sie haben L Unterlisten von jeweils I Elementen: Die ersten I Elemente werden L-1-mal hin und her kopiert, die zweiten I Elemente L-2 Mal und so weiter. Die Gesamtzahl der Kopien ist das I-fache der Summe von x für x von 1 bis L ausgeschlossen, d I * (L**2)/2. h .

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

Alex Martelli
quelle
486
Ich habe einen Test mit denselben Daten versucht, wobei itertools.chain.from_iterable: $ python -mtimeit -s'from itertools import chain; l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'list(chain.from_iterable(l))'. Es läuft etwas mehr als doppelt so schnell wie das verschachtelte Listenverständnis, das die schnellste der hier gezeigten Alternativen ist.
Intuition
274
Ich fand die Syntax schwer zu verstehen, bis mir klar wurde, dass man sie sich genau wie verschachtelte for-Schleifen vorstellen kann. für Unterliste in l: für Artikel in Unterliste: Ertrag Artikel
Rob Crowell
23
@ BorisChervenkov: Beachten Sie, dass ich den Aufruf eingepackt habe list(), um den Iterator in eine Liste zu integrieren.
Intuitioniert
163
[Blatt für Baum im Wald für Blatt im Baum] ist möglicherweise leichter zu verstehen und anzuwenden.
John Mee
80
@ Joel, eigentlich ist heutzutage list(itertools.chain.from_iterable(l))am besten - wie in anderen Kommentaren und Shawns Antwort bemerkt.
Alex Martelli
1568

Sie können verwenden itertools.chain():

import itertools
list2d = [[1,2,3], [4,5,6], [7], [8,9]]
merged = list(itertools.chain(*list2d))

Oder Sie können itertools.chain.from_iterable()Folgendes verwenden, ohne die Liste mit dem *Bediener entpacken zu müssen :

import itertools
list2d = [[1,2,3], [4,5,6], [7], [8,9]]
merged = list(itertools.chain.from_iterable(list2d))
Shawn Chin
quelle
13
Das *ist die schwierige Sache, die chainweniger einfach macht als das Listenverständnis. Sie müssen wissen, dass die Kette nur die als Parameter übergebenen Iterables zusammenfügt und das * bewirkt, dass die Liste der obersten Ebene zu Parametern erweitert wird, sodass chainalle diese Iterables zusammengefügt werden, aber nicht weiter absteigen. Ich denke, dies macht das Verständnis in diesem Fall lesbarer als die Verwendung von Ketten.
Tim Dierks
52
@ TimDierks: Ich bin nicht sicher, ob "dies erfordert, dass Sie die Python-Syntax verstehen" ein Argument gegen die Verwendung einer bestimmten Technik in Python ist. Sicher, eine komplexe Verwendung könnte verwirrend sein, aber der "splat" -Operator ist im Allgemeinen unter vielen Umständen nützlich, und dies verwendet ihn nicht auf besonders dunkle Weise. Wenn Sie alle Sprachfunktionen ablehnen, die für Anfänger nicht unbedingt offensichtlich sind, binden Sie eine Hand hinter Ihren Rücken. Sie können auch Listenverständnisse wegwerfen, wenn Sie gerade dabei sind. Benutzer mit anderen Hintergründen würden eine forSchleife finden, die immer appendoffensichtlicher wird.
ShadowRanger
Diese Antwort und andere Antworten hier ergeben ein falsches Ergebnis, wenn die oberste Ebene auch einen Wert enthält. zum Beispiel list = [["abc","bcd"],["cde","def"],"efg"]wird in einem Ausgang zur Folge["abc", "bcd", "cde", "def", "e", "f", "g"].
gouravkr
906

Anmerkung des Autors : Dies ist ineffizient. Aber Spaß, denn Monoide sind großartig. Es ist nicht für die Produktion von Python-Code geeignet.

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

Dies summiert nur die Elemente von iterable, die im ersten Argument übergeben wurden, und behandelt das zweite Argument als den Anfangswert der Summe (falls nicht angegeben, 0wird stattdessen verwendet und dieser Fall gibt Ihnen einen Fehler).

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

Beachten Sie, dass dies nur für Listen von Listen funktioniert. Für Listen mit Listen von Listen benötigen Sie eine andere Lösung.

Triptychon
quelle
100
Das ist ziemlich ordentlich und klug, aber ich würde es nicht benutzen, weil es verwirrend zu lesen ist.
Andrewrk
87
Dies ist ein Shlemiel, der Algorithmus des Malers joelonsoftware.com/articles/fog0000000319.html - unnötig ineffizient und unnötig hässlich.
Mike Graham
44
Die Append-Operation an Listen bildet eine Monoid, eine der bequemsten Abstraktionen, um eine +Operation im allgemeinen Sinne zu betrachten (nicht nur auf Zahlen beschränkt). Diese Antwort verdient also eine +1 von mir für die (korrekte) Behandlung von Listen als Monoid. Die Leistung ist jedoch
besorgniserregend
7
@andrewrk Nun, einige Leute denken, dass dies der sauberste Weg ist: youtube.com/watch?v=IOiZatlZtGU Diejenigen, die nicht verstehen, warum das cool ist, müssen nur ein paar Jahrzehnte warten, bis alle es so machen: ) Lassen Sie uns Programmiersprachen (und Abstraktionen) verwenden, die entdeckt und nicht erfunden werden. Monoid wird entdeckt.
Jhegedus
11
Dies ist aufgrund des quadratischen Aspekts der Summe ein sehr ineffizienter Weg.
Jean-François Fabre
460

Ich habe die meisten Lösungsvorschläge mit perfplot (einem meiner Lieblingsprojekte , im Wesentlichen einem Wrapper timeit) getestet und gefunden

functools.reduce(operator.iconcat, a, [])

Dies ist die schnellste Lösung, wenn viele kleine und wenige lange Listen verkettet sind. ( operator.iaddist gleich schnell.)

Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein


Code zur Reproduktion der Handlung:

import functools
import itertools
import numpy
import operator
import perfplot


def forfor(a):
    return [item for sublist in a for item in sublist]


def sum_brackets(a):
    return sum(a, [])


def functools_reduce(a):
    return functools.reduce(operator.concat, a)


def functools_reduce_iconcat(a):
    return functools.reduce(operator.iconcat, a, [])


def itertools_chain(a):
    return list(itertools.chain.from_iterable(a))


def numpy_flat(a):
    return list(numpy.array(a).flat)


def numpy_concatenate(a):
    return list(numpy.concatenate(a))


perfplot.show(
    setup=lambda n: [list(range(10))] * n,
    # setup=lambda n: [list(range(n))] * 10,
    kernels=[
        forfor,
        sum_brackets,
        functools_reduce,
        functools_reduce_iconcat,
        itertools_chain,
        numpy_flat,
        numpy_concatenate,
    ],
    n_range=[2 ** k for k in range(16)],
    xlabel="num lists (of length 10)",
    # xlabel="len lists (10 lists total)"
)
Nico Schlömer
quelle
25
Bei großen verschachtelten Listen ist 'list (numpy.array (a) .flat)' die schnellste unter allen oben genannten Funktionen.
Sara
Versucht mit Regex: 'Liste (map (int, re.findall (r "[\ w] +", str (a))))'. Geschwindigkeit ist etwas langsamer als numpy_concatenate
Justas
Gibt es eine Möglichkeit, einen 3-D-Perfplot zu erstellen? Anzahl der Arrays nach durchschnittlicher Größe des Arrays?
Leo
Ich liebe deine Lösung. Kurz, einfach und effizient :-)
ShadyMBA
181
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 extend()Methode in Ihrem Beispiel ändert sich, xanstatt einen nützlichen Wert zurückzugeben (der reduce()erwartet).

Ein schnellerer Weg, um die reduceVersion 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]
Greg Hewgill
quelle
19
reduce(operator.add, l)wäre der richtige Weg, um die reduceVersion zu machen. Eingebaute sind schneller als Lambdas.
Agf
3
@agf hier ist wie: * timeit.timeit('reduce(operator.add, l)', 'import operator; l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]', number=10000) 0.017956018447875977 * timeit.timeit('reduce(lambda x, y: x+y, l)', 'import operator; l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]', number=10000) 0.025218963623046875
lukmdo
8
Dies ist ein Shlemiel der Maler-Algorithmus joelonsoftware.com/articles/fog0000000319.html
Mike Graham
2
Dies kann nur für verwendet werden integers. Aber was ist, wenn die Liste enthält string?
Freddy
3
@Freddy: Die operator.addFunktion funktioniert sowohl für Ganzzahllisten als auch für Zeichenfolgenlisten gleich gut.
Greg Hewgill
120

Erfinden Sie das Rad nicht neu, wenn Sie Django verwenden :

>>> from django.contrib.admin.utils import flatten
>>> l = [[1,2,3], [4,5], [6]]
>>> flatten(l)
>>> [1, 2, 3, 4, 5, 6]

... Pandas :

>>> from pandas.core.common import flatten
>>> list(flatten(l))

... Itertools :

>>> import itertools
>>> flatten = itertools.chain.from_iterable
>>> list(flatten(l))

... Matplotlib

>>> from matplotlib.cbook import flatten
>>> list(flatten(l))

... Unipath :

>>> from unipath.path import flatten
>>> list(flatten(l))

... Setuptools :

>>> from setuptools.namespaces import flatten
>>> list(flatten(l))
Max Malysh
quelle
4
flatten = itertools.chain.from_iterablesollte die richtige Antwort sein
Geckos
3
gute Antwort! funktioniert auch für l = [[[1, 2, 3], [4, 5]], 5] bei Pandas
Markus Dutschke
1
Ich mag die Pandas-Lösung. Wenn Sie so etwas wie: haben list_of_menuitems = [1, 2, [3, [4, 5, [6]]]], ergibt sich Folgendes : [1, 2, 3, 4, 5, 6]. Was ich vermisse, ist die Abflachung.
Imjoseangel
115

Hier ist ein allgemeiner Ansatz, der für Zahlen , Zeichenfolgen , verschachtelte Listen und gemischte Container gilt.

Code

#from typing import Iterable 
from collections import Iterable                            # < py38


def flatten(items):
    """Yield items from any nested iterable; see Reference."""
    for x in items:
        if isinstance(x, Iterable) and not isinstance(x, (str, bytes)):
            for sub_x in flatten(x):
                yield sub_x
        else:
            yield x

Anmerkungen :

  • In Python 3 yield from flatten(x)kann ersetzenfor sub_x in flatten(x): yield sub_x
  • In Python 3.8, abstrakte Basisklassen sind bewegt von collection.abczum typingModul.

Demo

lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(flatten(lst))                                         # nested lists
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

mixed = [[1, [2]], (3, 4, {5, 6}, 7), 8, "9"]              # numbers, strs, nested & mixed
list(flatten(mixed))
# [1, 2, 3, 4, 5, 6, 7, 8, '9']

Referenz

  • Diese Lösung wurde nach einem Rezept in Beazley, D. und B. Jones modifiziert . Rezept 4.14, Python-Kochbuch, 3. Auflage, O'Reilly Media Inc. Sebastopol, CA: 2013.
  • Fand einen früheren SO-Beitrag , möglicherweise die ursprüngliche Demonstration.
Pylang
quelle
5
Ich habe so ziemlich das Gleiche geschrieben, weil ich Ihre Lösung nicht gesehen habe ... hier ist, wonach ich gesucht habe "Rekursiv vollständige Liste mehrerer Listen reduzieren" ... (+1)
Martin Thoma
3
@ MartinThoma Sehr geschätzt. Zu Ihrer Information: Wenn es für Sie üblich ist, verschachtelte Iterables zu reduzieren, gibt es einige Pakete von Drittanbietern, die dies gut handhaben. Dies kann verhindern, dass das Rad neu erfunden wird. Ich habe more_itertoolsunter anderem in diesem Beitrag diskutiert. Prost.
Pylang
Vielleicht traversekönnte auch ein guter Name für diese Art eines Baumes sein, während ich ihn für diese Antwort weniger universell halten würde, indem ich mich an verschachtelte Listen halte .
Wolf
Sie können prüfen, if hasattr(x, '__iter__')anstatt zu importieren / zu prüfen, Iterableund dies schließt auch Zeichenfolgen aus.
Ryan Allen
Der obige Code scheint nicht zu funktionieren, wenn eine der verschachtelten Listen eine Liste von Zeichenfolgen enthält. [1, 2, [3, 4], [4], [], 9, 9,5, 'ssssss', ['str', 'sss', 'ss'], [3, 4, 5]] Ausgabe: - [1, 2, 3, 4, 4, 9, 9,5, 'ssssss', 3, 4, 5]
sunnyX
51

Wenn Sie eine Datenstruktur reduzieren möchten, bei der Sie nicht wissen, wie tief sie verschachtelt ist, können Sie 1 verwendeniteration_utilities.deepflatten

>>> from iteration_utilities import deepflatten

>>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
>>> list(deepflatten(l, depth=1))
[1, 2, 3, 4, 5, 6, 7, 8, 9]

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

listDa es sich um einen Generator handelt, müssen Sie das Ergebnis in a umwandeln oder explizit darüber iterieren.


Um nur eine Ebene zu reduzieren und wenn jedes der Elemente selbst iterierbar ist, können Sie auch verwenden, iteration_utilities.flattenwas selbst nur eine dünne Hülle ist itertools.chain.from_iterable:

>>> from iteration_utilities import flatten
>>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
>>> list(flatten(l))
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Nur um einige Zeitangaben hinzuzufügen (basierend auf der Antwort von Nico Schlömer, die die in dieser Antwort dargestellte Funktion nicht enthielt):

Geben Sie hier die Bildbeschreibung ein

Es ist ein Log-Log-Plot, um den großen Wertebereich zu berücksichtigen. Zum qualitativen Denken: Niedriger ist besser.

Die Ergebnisse zeigen , dass , wenn die iterable nur wenige innere Iterables enthalten dann sumam schnellsten sein, aber für langen Iterables nur den itertools.chain.from_iterable, iteration_utilities.deepflattenoder das verschachtelten Verständnis haben angemessene Leistung mit itertools.chain.from_iterableder schnellsten zu sein (wie schon bemerkt , von Nico Schlömer).

from itertools import chain
from functools import reduce
from collections import Iterable  # or from collections.abc import Iterable
import operator
from iteration_utilities import deepflatten

def nested_list_comprehension(lsts):
    return [item for sublist in lsts for item in sublist]

def itertools_chain_from_iterable(lsts):
    return list(chain.from_iterable(lsts))

def pythons_sum(lsts):
    return sum(lsts, [])

def reduce_add(lsts):
    return reduce(lambda x, y: x + y, lsts)

def pylangs_flatten(lsts):
    return list(flatten(lsts))

def flatten(items):
    """Yield items from any nested iterable; see REF."""
    for x in items:
        if isinstance(x, Iterable) and not isinstance(x, (str, bytes)):
            yield from flatten(x)
        else:
            yield x

def reduce_concat(lsts):
    return reduce(operator.concat, lsts)

def iteration_utilities_deepflatten(lsts):
    return list(deepflatten(lsts, depth=1))


from simple_benchmark import benchmark

b = benchmark(
    [nested_list_comprehension, itertools_chain_from_iterable, pythons_sum, reduce_add,
     pylangs_flatten, reduce_concat, iteration_utilities_deepflatten],
    arguments={2**i: [[0]*5]*(2**i) for i in range(1, 13)},
    argument_name='number of inner lists'
)

b.plot()

1 Haftungsausschluss: Ich bin der Autor dieser Bibliothek

MSeifert
quelle
sumfunktioniert nicht mehr mit beliebigen Sequenzen, wie es anfängt 0, und macht functools.reduce(operator.add, sequences)den Ersatz (sind wir nicht froh, dass sie reduceaus den integrierten Elementen entfernt wurden?). Wenn die Typen bekannt sind, ist die Verwendung möglicherweise schneller type.__add__.
Yann Vernier
@YannVernier Danke für die Information. Ich dachte, ich hätte diese Benchmarks unter Python 3.6 ausgeführt und es hat funktioniert sum. Wissen Sie zufällig, bei welchen Python-Versionen es nicht mehr funktioniert?
MSeifert
Ich habe mich etwas geirrt. 0ist nur der Standardstartwert, also funktioniert es, wenn man das Startargument verwendet , um mit einer leeren Liste zu beginnen ... aber es werden immer noch Sonderzeichenfolgen verwendet und ich werde angewiesen, join zu verwenden. Es implementiert foldlstatt foldl1. Das gleiche Problem taucht in 2.7 auf.
Yann Vernier
39

Ich nehme meine Aussage zurück. Summe ist nicht der Gewinner. Obwohl es schneller ist, wenn die Liste klein ist. Bei größeren Listen nimmt die Leistung jedoch erheblich ab.

>>> timeit.Timer(
        '[item for sublist in l for item in sublist]',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10000'
    ).timeit(100)
2.0440959930419922

Die Summenversion läuft noch länger als eine Minute und wurde noch nicht verarbeitet!

Für mittlere Listen:

>>> timeit.Timer(
        '[item for sublist in l for item in sublist]',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10'
    ).timeit()
20.126545906066895
>>> timeit.Timer(
        'reduce(lambda x,y: x+y,l)',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10'
    ).timeit()
22.242258071899414
>>> timeit.Timer(
        'sum(l, [])',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10'
    ).timeit()
16.449732065200806

Mit kleinen Listen und Zeitangaben: Nummer = 1000000

>>> timeit.Timer(
        '[item for sublist in l for item in sublist]',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]'
    ).timeit()
2.4598159790039062
>>> timeit.Timer(
        'reduce(lambda x,y: x+y,l)',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]'
    ).timeit()
1.5289170742034912
>>> timeit.Timer(
        'sum(l, [])',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]'
    ).timeit()
1.0598428249359131
Nadia Alramli
quelle
23
für eine wirklich winzige Liste, z. B. eine mit 3 Unterlisten, vielleicht - aber da die Leistung der Summe mit O (N ** 2) geht, während das Listenverständnis mit O (N) geht, wird nur ein wenig Erweitern der Eingabeliste die Dinge umkehren - - in der Tat wird der LC "unendlich schneller" sein als die Summe an der Grenze, wenn N wächst. Ich war dafür verantwortlich, sum zu entwerfen und seine erste Implementierung in der Python-Laufzeit durchzuführen, und ich wünschte immer noch, ich hätte einen Weg gefunden, es effektiv auf das Summieren von Zahlen (was es wirklich gut kann) zu beschränken und das "attraktive Ärgernis" zu blockieren, das es den Menschen bietet wer will Listen "summieren" ;-).
Alex Martelli
38

Es scheint eine Verwechslung mit zu geben operator.add! Wenn Sie zwei Listen zusammenfügen, lautet der richtige Begriff dafür concatnicht hinzufügen. operator.concatist das, was Sie verwenden müssen.

Wenn Sie funktional denken, ist es so einfach:

>>> from functools import reduce
>>> list2d = ((1, 2, 3), (4, 5, 6), (7,), (8, 9))
>>> reduce(operator.concat, list2d)
(1, 2, 3, 4, 5, 6, 7, 8, 9)

Sie sehen, dass der Sequenztyp reduziert wird. Wenn Sie also ein Tupel angeben, erhalten Sie ein Tupel zurück. Versuchen wir es mit einer Liste ::

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

Aha, du bekommst eine Liste zurück.

Wie wäre es mit Leistung ::

>>> list2d = [[1, 2, 3],[4, 5, 6], [7], [8, 9]]
>>> %timeit list(itertools.chain.from_iterable(list2d))
1000000 loops, best of 3: 1.36 µs per loop

from_iterableist ziemlich schnell! Aber es ist kein Vergleich, mit dem man reduzieren kann concat.

>>> list2d = ((1, 2, 3),(4, 5, 6), (7,), (8, 9))
>>> %timeit reduce(operator.concat, list2d)
1000000 loops, best of 3: 492 ns per loop
Meitham
quelle
1
Hmm um fair zu sein, zweites Beispiel sollte auch Liste sein (oder erstes Tupel?)
Mr_and_Mrs_D
2
Die Verwendung derart kleiner Eingaben ist kein fairer Vergleich. Für 1000 Sequenzen mit einer Länge von 1000 erhalte ich 0,037 Sekunden für list(chain.from_iterable(...))und 2,5 Sekunden für reduce(concat, ...). Das Problem ist, dass es reduce(concat, ...)eine quadratische Laufzeit hat, während chaines linear ist.
Kaya3
33

Warum verwenden Sie verlängern?

reduce(lambda x, y: x+y, l)

Dies sollte gut funktionieren.

Andrea Ambu
quelle
7
für Python3from functools import reduce
Andorov
Tut mir leid, das ist wirklich langsam, siehe Rest der Antworten
Mr_and_Mrs_D
Dies ist bei weitem die am einfachsten zu verstehende und dennoch kurze Lösung, die auf Python 2 und 3 funktioniert. Mir ist klar, dass sich viele Python-Leute in der Datenverarbeitung befinden, wo große Datenmengen verarbeitet werden müssen, und sich daher sehr um die Geschwindigkeit kümmern, aber wenn Sie schreiben ein Shell-Skript und haben nur ein paar Dutzend Elemente in ein paar Unterlisten, dann ist dies perfekt.
Asfand Qazi
27

Erwägen Sie die Installation des more_itertoolsPakets.

> pip install more_itertools

Es wird mit einer Implementierung für flatten( Quelle , aus den itertools-Rezepten ) geliefert :

import more_itertools


lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(more_itertools.flatten(lst))
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

Ab Version 2.4 können Sie kompliziertere, verschachtelte Iterables mit more_itertools.collapse( Quelle , beigesteuert von abarnet) reduzieren.

lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(more_itertools.collapse(lst)) 
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

lst = [[1, 2, 3], [[4, 5, 6]], [[[7]]], 8, 9]              # complex nesting
list(more_itertools.collapse(lst))
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
Pylang
quelle
Tatsächlich. Dies sollte die akzeptierte Antwort sein
Brunetton
Wenn Sie es sich leisten können, Ihrem Projekt ein Paket hinzuzufügen, ist diese Antwort am besten
viddik13
22

Der Grund, warum Ihre Funktion nicht funktioniert hat, ist, dass die Erweiterung ein Array an Ort und Stelle erweitert und nicht zurückgibt. Sie können immer noch x von Lambda zurückgeben, indem Sie Folgendes verwenden:

reduce(lambda x,y: x.extend(y) or x, l)

Hinweis: Erweitern ist in Listen effizienter als +.

Igor Krivokon
quelle
7
extendbesser als verwendet wird newlist = [], extend = newlist.extend, for sublist in l: extend(l)wie es die (recht großen) Overhead von der vermeidet lambda, das Attribut - Lookup xund der or.
Agf
für Python 3 hinzufügenfrom functools import reduce
Markus Dutschke
17
def flatten(l, a):
    for i in l:
        if isinstance(i, list):
            flatten(i, a)
        else:
            a.append(i)
    return a

print(flatten([[[1, [1,1, [3, [4,5,]]]], 2, 3], [4, 5],6], []))

# [1, 1, 1, 3, 4, 5, 2, 3, 4, 5, 6]
Anil
quelle
def flatten(l, a=None): if a is None: a = [][...]
Poik
16

Rekursive Version

x = [1,2,[3,4],[5,[6,[7]]],8,9,[10]]

def flatten_list(k):
    result = list()
    for i in k:
        if isinstance(i,list):

            #The isinstance() function checks if the object (first argument) is an 
            #instance or subclass of classinfo class (second argument)

            result.extend(flatten_list(i)) #Recursive call
        else:
            result.append(i)
    return result

flatten_list(x)
#result = [1,2,3,4,5,6,7,8,9,10]
Saurabh Singh
quelle
1
schön, keine Importe erforderlich und es ist klar, was es tut ... eine Liste abflachen, Punkt :)
Goran B.
1
einfach genial!
Sachin Sharma
15

matplotlib.cbook.flatten() funktioniert für verschachtelte Listen, auch wenn sie tiefer als das Beispiel verschachtelt sind.

import matplotlib
l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
print(list(matplotlib.cbook.flatten(l)))
l2 = [[1, 2, 3], [4, 5, 6], [7], [8, [9, 10, [11, 12, [13]]]]]
print list(matplotlib.cbook.flatten(l2))

Ergebnis:

[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]

Dies ist 18x ​​schneller als der Unterstrich ._. Abflachen:

Average time over 1000 trials of matplotlib.cbook.flatten: 2.55e-05 sec
Average time over 1000 trials of underscore._.flatten: 4.63e-04 sec
(time for underscore._)/(time for matplotlib.cbook) = 18.1233394636
EL_DON
quelle
14

Die akzeptierte Antwort funktionierte bei mir nicht, wenn es um textbasierte Listen variabler Länge ging. Hier ist ein alternativer Ansatz, der für mich funktioniert hat.

l = ['aaa', 'bb', 'cccccc', ['xx', 'yyyyyyy']]

Akzeptierte Antwort, die nicht funktioniert hat:

flat_list = [item for sublist in l for item in sublist]
print(flat_list)
['a', 'a', 'a', 'b', 'b', 'c', 'c', 'c', 'c', 'c', 'c', 'xx', 'yyyyyyy']

Neue vorgeschlagene Lösung, die für mich funktioniert hat:

flat_list = []
_ = [flat_list.extend(item) if isinstance(item, list) else flat_list.append(item) for item in l if item]
print(flat_list)
['aaa', 'bb', 'cccccc', 'xx', 'yyyyyyy']
user9074332
quelle
13

Ein schlechtes Merkmal der obigen Funktion von Anil ist, dass der Benutzer das zweite Argument immer manuell als leere Liste angeben muss []. Dies sollte stattdessen eine Standardeinstellung sein. Aufgrund der Funktionsweise von Python-Objekten sollten diese innerhalb der Funktion und nicht in den Argumenten festgelegt werden.

Hier ist eine Arbeitsfunktion:

def list_flatten(l, a=None):
    #check a
    if a is None:
        #initialize with empty list
        a = []

    for i in l:
        if isinstance(i, list):
            list_flatten(i, a)
        else:
            a.append(i)
    return a

Testen:

In [2]: lst = [1, 2, [3], [[4]],[5,[6]]]

In [3]: lst
Out[3]: [1, 2, [3], [[4]], [5, [6]]]

In [11]: list_flatten(lst)
Out[11]: [1, 2, 3, 4, 5, 6]
Deleet
quelle
13

Folgendes scheint mir am einfachsten zu sein:

>>> import numpy as np
>>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
>>> print (np.concatenate(l))
[1 2 3 4 5 6 7 8 9]
Teufel im Detail
quelle
Funktioniert nicht für Listen mit unterschiedlichen Dimensionen. -1
nurub
10

Man kann auch NumPys Wohnung benutzen :

import numpy as np
list(np.array(l).flat)

Edit 11/02/2016: Funktioniert nur, wenn Unterlisten identische Dimensionen haben.

mdh
quelle
Wäre das die optimale Lösung?
RetroCode
6

Sie können numpy verwenden:
flat_list = list(np.concatenate(list_of_list))

A. Attia
quelle
Dies funktioniert auch für numerische, Zeichenfolgen und gemischte Listen
Nitin
2
Schlägt für ungleichmäßig verschachtelte Daten [1, 2, [3], [[4]], [5, [6]]]
fehl
5

Wenn Sie bereit sind , eine winzige Menge an Geschwindigkeit für eine sauberere Aussehen zu geben, dann könnten Sie numpy.concatenate().tolist()oder numpy.concatenate().ravel().tolist():

import numpy

l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]] * 99

%timeit numpy.concatenate(l).ravel().tolist()
1000 loops, best of 3: 313 µs per loop

%timeit numpy.concatenate(l).tolist()
1000 loops, best of 3: 312 µs per loop

%timeit [item for sublist in l for item in sublist]
1000 loops, best of 3: 31.5 µs per loop

Weitere Informationen finden Sie hier in den Dokumenten numpy.concatenate und numpy.ravel

mkultra
quelle
1
Funktioniert nicht für ungleichmäßig verschachtelte Listen wie[1, 2, [3], [[4]], [5, [6]]]
EL_DON
5

Schnellste Lösung, die ich gefunden habe (für große Liste sowieso):

import numpy as np
#turn list into an array and flatten()
np.array(l).flatten()

Erledigt! Sie können es natürlich wieder in eine Liste verwandeln, indem Sie list (l) ausführen.

Canuck
quelle
1
Dies ist falsch. Durch das Reduzieren werden die Abmessungen des nd-Arrays auf eins reduziert, die darin enthaltenen Listen jedoch nicht als eins verkettet.
Ando Jurai
5

Einfacher Code für underscore.pyPaketlüfter

from underscore import _
_.flatten([[1, 2, 3], [4, 5, 6], [7], [8, 9]])
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

Es löst alle Abflachungsprobleme (kein Listenelement oder komplexe Verschachtelung).

from underscore import _
# 1 is none list item
# [2, [3]] is complex nesting
_.flatten([1, [2, [3]], [4, 5, 6], [7], [8, 9]])
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

Sie können underscore.pymit pip installieren

pip install underscore.py
Vu Anh
quelle
Ebenso können Sie Pydash verwenden . Ich finde diese Version viel besser lesbar als das Listenverständnis oder andere Antworten.
Gliemezis
2
Das ist super langsam.
Nico Schlömer
2
Warum hat es ein Modul namens _? Das scheint ein schlechter Name zu sein. Siehe stackoverflow.com/a/5893946/6605826
EL_DON
2
@EL_DON: Auf der Readme-Seite von underscore.py "Underscore.py ist ein Python-Port der exzellenten Javascript-Bibliothek underscore.js". Ich denke, das ist der Grund für diesen Namen. Und ja, es ist kein guter Name für Python
Vu Anh
5
def flatten(alist):
    if alist == []:
        return []
    elif type(alist) is not list:
        return [alist]
    else:
        return flatten(alist[0]) + flatten(alist[1:])
englealuze
quelle
Schlägt für Python2.7 für die verschachtelte Beispielliste in der Frage [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
fehl
@EL_DON getestet auf Python 2.7.5. es funktioniert gut
englealuze
5

Hinweis : Das Folgende gilt für Python 3.3+, da es verwendet wird yield_from. sixist auch ein Paket von Drittanbietern, obwohl es stabil ist. Alternativ könnten Sie verwenden sys.version.


Im Fall von obj = [[1, 2,], [3, 4], [5, 6]]sind alle Lösungen hier gut, einschließlich Listenverständnis und itertools.chain.from_iterable.

Betrachten Sie jedoch diesen etwas komplexeren Fall:

>>> obj = [[1, 2, 3], [4, 5], 6, 'abc', [7], [8, [9, 10]]]

Hier gibt es mehrere Probleme:

  • Ein Element 6ist nur ein Skalar; Es ist nicht iterierbar, daher schlagen die oben genannten Routen hier fehl.
  • Ein Element, 'abc', ist technisch iterable (alle strs). Wenn Sie jedoch ein wenig zwischen den Zeilen lesen, möchten Sie es nicht als solches behandeln - Sie möchten es als ein einzelnes Element behandeln.
  • Das letzte Element [8, [9, 10]]ist selbst eine verschachtelte Iteration. Grundlegendes Listenverständnis und chain.from_iterablenur "1 Ebene tiefer" extrahieren.

Sie können dies wie folgt beheben:

>>> from collections import Iterable
>>> from six import string_types

>>> def flatten(obj):
...     for i in obj:
...         if isinstance(i, Iterable) and not isinstance(i, string_types):
...             yield from flatten(i)
...         else:
...             yield i


>>> list(flatten(obj))
[1, 2, 3, 4, 5, 6, 'abc', 7, 8, 9, 10]

Hier überprüfen Sie, ob das Unterelement (1) mit Iterableeinem ABC von iterierbar ist , itertoolsmöchten aber auch sicherstellen, dass (2) das Element nicht "stringartig" ist.

Brad Solomon
quelle
1
Wenn Sie immer noch an Python 2-Kompatibilität interessiert sind, wechseln Sie yield fromzu einer forSchleife, z. B.for x in flatten(i): yield x
pylang
5
flat_list = []
for i in list_of_list:
    flat_list+=i

Dieser Code funktioniert auch gut, da er die Liste nur vollständig erweitert. Es ist zwar sehr ähnlich, hat aber nur eine for-Schleife. Es ist also weniger komplex als das Hinzufügen von 2 for-Schleifen.

Deepak Yadav
quelle
5
from nltk import flatten

l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
flatten(l)

Der Vorteil dieser Lösung gegenüber den meisten anderen hier ist, dass Sie eine Liste haben wie:

l = [1, [2, 3], [4, 5, 6], [7], [8, 9]]

Während die meisten anderen Lösungen einen Fehler auslösen, werden sie von dieser Lösung behandelt.

Alijy
quelle
Die Frage gibt eine "Liste von Listen" an, aber Ihre Beispielliste enthält ein Nicht-Listenelement. Die meisten anderen Lösungen halten an der ursprünglichen Frage fest. Ihre Lösung löst ein größeres Problem, erfordert jedoch auch ein Nicht-Basis-Python-Paket (nltk), das zuerst installiert werden muss.
Simonobo
4

Dies ist vielleicht nicht der effizienteste Weg, aber ich dachte, ich würde einen Einzeiler (eigentlich einen Zweiliner) einsetzen. Beide Versionen arbeiten mit verschachtelten Listen mit beliebiger Hierarchie und nutzen Sprachfunktionen (Python3.5) und Rekursion.

def make_list_flat (l):
    flist = []
    flist.extend ([l]) if (type (l) is not list) else [flist.extend (make_list_flat (e)) for e in l]
    return flist

a = [[1, 2], [[[[3, 4, 5], 6]]], 7, [8, [9, [10, 11], 12, [13, 14, [15, [[16, 17], 18]]]]]]
flist = make_list_flat(a)
print (flist)

Die Ausgabe ist

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]

Dies funktioniert in erster Linie in der Tiefe. Die Rekursion wird fortgesetzt, bis ein Nicht-Listenelement gefunden wird. Anschließend wird die lokale Variable erweitert flistund auf das übergeordnete Element zurückgesetzt. Immer wenn flistes zurückgegeben wird, wird es flistim Listenverständnis auf das der Eltern erweitert . Daher wird im Stammverzeichnis eine flache Liste zurückgegeben.

Die obige Liste erstellt mehrere lokale Listen und gibt diese zurück, die zum Erweitern der Liste der Eltern verwendet werden. Ich denke, der Weg dahin könnte darin bestehen, ein Gloabl zu schaffen flist, wie unten.

a = [[1, 2], [[[[3, 4, 5], 6]]], 7, [8, [9, [10, 11], 12, [13, 14, [15, [[16, 17], 18]]]]]]
flist = []
def make_list_flat (l):
    flist.extend ([l]) if (type (l) is not list) else [make_list_flat (e) for e in l]

make_list_flat(a)
print (flist)

Die Ausgabe ist wieder

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]

Obwohl ich mir derzeit nicht sicher bin, wie effizient es ist.

Phoxis
quelle
Warum verlängern ([l]) anstatt (l) anhängen?
Maciek
3

Ein weiterer ungewöhnlicher Ansatz, der für hetero- und homogene Listen von ganzen Zahlen funktioniert:

from typing import List


def flatten(l: list) -> List[int]:
    """Flatten an arbitrary deep nested list of lists of integers.

    Examples:
        >>> flatten([1, 2, [1, [10]]])
        [1, 2, 1, 10]

    Args:
        l: Union[l, Union[int, List[int]]

    Returns:
        Flatted list of integer
    """
    return [int(i.strip('[ ]')) for i in str(l).split(',')]
tharndt
quelle
Das ist nur eine kompliziertere und etwas langsamere Art von dem, was before3000 bereits zuvor gepostet hat. Ich habe seinen Vorschlag gestern neu erfunden, daher scheint dieser Ansatz heutzutage sehr beliebt zu sein;)
Darkonaut
Nicht ganz: wierd_list = [[1, 2, 3], [4, 5, 6], [7], [8, 9], 10]>>nice_list=[1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 0]
tharndt
Mein Code als ein Liner wäre: flat_list = [int(e.replace('[','').replace(']','')) for e in str(deep_list).split(',')]
11.
1
Sie haben in der Tat Recht +1, der Vorschlag von ᴡʜᴀᴄᴋᴀᴍᴀᴅᴏᴏᴅʟᴇ3000 funktioniert nicht mit mehrstelligen Zahlen. Ich habe dies auch vorher nicht getestet, obwohl es offensichtlich sein sollte. Sie könnten Ihren Code vereinfachen und schreiben [int(e.strip('[ ]')) for e in str(deep_list).split(',')]. Aber ich würde vorschlagen, bei Deleets Vorschlag für echte Anwendungsfälle zu bleiben. Es enthält keine Hacky-Typ-Transformationen, ist schneller und vielseitiger, da es natürlich auch Listen mit gemischten Typen verarbeitet.
Darkonaut
2
Unglücklicherweise nicht. Aber ich habe diesen Code kürzlich hier gesehen: Python Practice Book 6.1.2
am