Frage

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

Das kann ich in einem machen for Schleife, aber vielleicht gibt es einen coolen „Einzeiler“?Ich habe es mit versucht reduzieren, aber ich erhalte 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'
War es hilfreich?

Lösung

eine Liste von Listen l gegeben,

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

Das bedeutet:

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

ist schneller als die Verknüpfungen bisher geschrieben. (l ist die Liste zu glätten.)

Hier ist die entsprechende Funktion:

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

Als Beweis können Sie das timeit Modul 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 basierend auf + (einschließlich der impliziten Verwendung in sum) sind, notwendigerweise O(L**2) wenn L Sublisten gibt es - wie das Zwischenergebnis-Liste wird immer länger, sollte bei jedem ein neues Zwischenergebnis Listenobjekt wird (sowie ein paar neue am Ende hinzugefügt) zugeordnet, und alle Elemente in dem vorherigen müssen Zwischenergebnis kopiert werden. Also, für Einfachheit und ohne tatsächlichen Verlust der Allgemeinheit, sagen Sie L Sublisten I Einzelteile haben jeweils: die ersten ich Elemente kopiert hin und her L-1-mal, den zweiten I Artikel L-2-mal, und so weiter; Gesamtanzahl der Kopien ist I-mal die Summe von x für x von 1 bis L ausgeschlossen, d.h. I * (L**2)/2.

Die Liste Verständnis erzeugt nur eine Liste, einmal und kopiert jedes Element über (von seinem ursprünglichen Wohnort in der Ergebnisliste) auch genau einmal.

Andere Tipps

Sie können mit itertools.chain() :

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

oder auf Python> = 2.6, verwenden Sie itertools.chain.from_iterable() , die erfordert nicht die Liste Auspacken:

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

Dieser Ansatz ist wohl besser lesbar als [item for sublist in l for item in sublist] und scheint schneller zu sein:

[me@home]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99;import itertools' 'list(itertools.chain.from_iterable(l))'
10000 loops, best of 3: 24.2 usec per loop
[me@home]$ 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: 45.2 usec per loop
[me@home]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'sum(l, [])'
1000 loops, best of 3: 488 usec per loop
[me@home]$ 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: 522 usec per loop
[me@home]$ python --version
Python 2.7.3

Hinweis des Autors : Das ist ineffizient. Aber Spaß, weil Monoide sind genial. Es ist nicht geeignet für die Produktion Python-Code.

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

Dieses fasst nur die Elemente der iterable im ersten Argument übergeben, zweite Argument als Anfangswert der Summe der Behandlung (wenn nicht angegeben, 0 wird verwendet, statt und dieser Fall wird Ihnen ein Fehler).

Weil Sie verschachtelte Listen summieren, die Sie tatsächlich [1,3]+[2,4] als Folge des sum([[1,3],[2,4]],[]) erhalten, die [1,3,2,4] gleich ist.

Beachten Sie, dass auf den Listen von Listen nur funktioniert. Für Listen von Listen von Listen, werden Sie eine andere Lösung benötigen.

Ich testete die meisten vorgeschlagenen Lösungen mit perfplot (ein Lieblingsprojekt von mir, im Wesentlichen ein Wrapper um timeit) und fand

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

die schnellste Lösung. (operator.iadd ist gleich schnell.)

 image description hier


Code, um das Grundstück zu reproduzieren:

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,
    kernels=[
        forfor, sum_brackets, functools_reduce, functools_reduce_iconcat,
        itertools_chain, numpy_flat, numpy_concatenate
        ],
    n_range=[2**k for k in range(16)],
    logx=True,
    logy=True,
    xlabel='num lists'
    )
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 modifiziert x stattdessen einen nützlichen Wert der Rücksendung (die reduce() erwartet).

Ein schneller Weg, um die reduce Version zu tun 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]

Hier ist ein allgemeiner Ansatz, der auf Zahlen gilt: , Strings , verschachtelt Listen und gemischt Container.

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

Notizen :

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 aus einem Rezept in Beazley, D. und B. Jones modifiziert. Rezept 4.14, Python-Kochbuch, 3. Auflage, O'Reilly Media Inc. Sebastopol, Kalifornien:.. 2013
  • Gefunden eine frühere SO Post , möglicherweise die ursprüngliche Demonstration.

Wenn Sie eine Datenstruktur glätten wollen, wo Sie nicht wissen, wie tief sie verschachtelt ist man verwenden könnte? iteration_utilities.deepflatten 1

>>> 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]

Es ist ein Generator, so dass Sie das Ergebnis in einen list werfen müssen oder explizit über sie iterieren.


Um nur eine Ebene abflachen und wenn jedes der Elemente selbst Iterable Sie können auch verwenden iteration_utilities.flatten , die selbst nur eine dünne Hülle um 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 einige Timings hinzufügen (basierend auf Nico Schlömer Antwort, die nicht die Funktion in dieser Antwort präsentiert haben ist):

 image description hier

Es ist ein log-log-Diagramm für den großen Bereich zur Aufnahme von Werten aufgespannt. Für die qualitative Argumentation: weniger ist besser

.

Die Ergebnisse zeigen, dass, wenn die iterable nur wenige innere Iterables enthalten dann sum schnellsten sein, aber für langen Iterables nur die itertools.chain.from_iterable, iteration_utilities.deepflatten oder das verschachtelte Verständnis hat angemessene Leistung mit itertools.chain.from_iterable die 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 Disclaimer: Ich bin der Autor dieser Bibliothek

Ich nehme meine Aussage zurück. Summe ist nicht der Gewinner. Obwohl es schneller ist, wenn die Liste klein. Aber die Leistung verschlechtert sich deutlich bei größeren 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]] * 10000'
    ).timeit(100)
2.0440959930419922

Die Summe Version noch für mehr als eine Minute läuft und es noch nicht getan hat, die Verarbeitung!

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 timeit: Anzahl = 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

Es scheint eine Verwechslung mit operator.add zu sein! Wenn Sie zwei Listen zusammen fügen, die korrekte Bezeichnung für das concat ist, fügen Sie nicht. operator.concat ist, was Sie verwenden müssen.

Wenn Sie funktional denken, es ist so einfach, wie dies ::

>>> 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 Hinsicht den Sequenztyp reduzieren, so dass, wenn Sie ein Tupel liefern, erhalten Sie ein Tupel zurück. Beginnen wir mit einer Liste versuchen ::

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

Aha, erhalten Sie zurück eine Liste.

Wie wäre 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_iterable ist ziemlich schnell! Aber es ist kein Vergleich mit concat zu reduzieren.

>>> list2d = ((1, 2, 3),(4, 5, 6), (7,), (8, 9))
>>> %timeit reduce(operator.concat, list2d)
1000000 loops, best of 3: 492 ns per loop

Warum verwenden Sie verlängern?

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

Das sollte funktionieren.

Betrachten Sie

Installieren des more_itertools Paket.

> pip install more_itertools

Es wird mit einer Implementierung für flatten ( Quelle , von der itertools Rezepte ):

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, Sie glätten können komplizierter, verschachtelten Iterables mit Quelle , trugen durch abarnet).

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]

Der Grund Ihrer Funktion nicht funktioniert hat: die in-place erstreckt Array erweitern und es nicht zurück. Sie können immer noch x von Lambda zurückgeben, einigen Trick:

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

. Hinweis: erweitern ist effizienter als auf Listen +

Setzen Sie das Rad nicht neu erfinden, wenn Sie mit Django :

>>> 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))
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]

Eine schlechte Eigenschaft von Anils Funktion oben ist, dass es den Benutzer erfordert immer manuell, um das zweite Argument angeben eine leere Liste [] zu sein. Dies sollte stattdessen eine Standard sein. Aufgrund der Art und Weise sollten diese Python-Objekten arbeiten, innerhalb der Funktion eingestellt werden, nicht in den Argumenten.

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

Test:

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]

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

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]

Das ist 18x ​​schneller als underscore._.flatten:

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

Die akzeptierte Antwort nicht für mich arbeiten, wenn sie mit textbasierten Listen variabler Länge handelt. Hier ist ein alternativer Ansatz, die für mich arbeiten.

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

akzeptierte Antwort, die hat nicht Arbeit:

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']

New vorgeschlagene Lösung, dass haben Arbeit für mich:

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']

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]

scheint Nach einfachsten zu mir:

>>> 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]

Man kann auch verwenden NumPy Wohnung :

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

Bearbeiten 2016.11.02: Dies funktioniert nur, wenn Sublisten identische Abmessungen

.

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

Einfacher Code für underscore.py Paket Fan

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 Probleme abflachen (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 installieren underscore.py mit pip

pip install underscore.py
def flatten(alist):
    if alist == []:
        return []
    elif type(alist) is not list:
        return [alist]
    else:
        return flatten(alist[0]) + flatten(alist[1:])
flat_list = []
for i in list_of_list:
    flat_list+=i

auch Dieser Code funktioniert gut, da es nur die Liste all Art und Weise erweitern. Obwohl es viel ähnlich ist, aber haben nur eine for-Schleife. So It weniger Komplexität hat als 2 Zugabe für Schleifen.

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() verwenden:

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

Sie können mehr hier in der Dokumentation erfahren Sie numpy.concatenate und numpy.ravel

Schnellste Lösung I (für große Liste sowieso) gefunden zu haben:

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

Fertig! Sie können es natürlich umkehren in eine Liste von Liste Ausführen (l)

Dies ist möglicherweise nicht der effizienteste Weg sein, aber ich dachte, einen Einzeiler (eigentlich ein Zweizeiler) zu setzen. Beide Versionen werden auf beliebigen Hierarchie verschachtelt Listen arbeiten und nutzt 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 einer Tiefen ersten Art und Weise. Die Rekursion geht nach unten, bis es eine nicht-Listenelement befindet, erstreckt sich dann die lokale Variable flist und rollt dann zurück, um es an die Mutter. Jedes Mal, wenn flist zurückgeführt wird, wird es an die Eltern flist in der Liste Verständnis erweitert. Daher an der Wurzel, eine flache Liste zurückgegeben wird.

Das oben schafft man mehrere lokale Listen und gibt sie, die die Eltern Liste verwendet zu verlängern. Ich denke, der Weg, um dafür kann eine gloabl flist, wie weiter unten erstellen.

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 zu diesem Zeitpunkt über die Effizienz nicht sicher bin.

Hinweis : hier gilt für Python 3.3+, weil es verwendet yield_from . six ist auch ein Dritt Paket, obwohl es stabil ist. Alternativ können Sie sys.version verwenden.


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

Allerdings betrachtet diese etwas komplexen Fall:

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

Es gibt mehrere Probleme hier:

  • Ein Element, 6, ist nur ein Skalar; es ist nicht durchsuchbar, so dass die oben genannten Strecken werden hier versagen.
  • Ein Element, 'abc', ist technisch iterable (alle strs sind). Doch zwischen den Zeilen ein bisschen lesen, Sie nicht wollen, es als solches behandeln -. Sie es als ein einzelnes Element behandeln wollen
  • Das letzte Element, [8, [9, 10]] ist selbst eine verschachtelte iterable. Grundliste Verständnis und chain.from_iterable nur extrahieren „1 Ebene nach unten.“

Sie können hier Abhilfe zu schaffen, wie folgt:

>>> 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, dass das Unterelement (1) ist iterable mit Iterable , ein ABC von itertools, sondern will auch sicherstellen, dass (2) das Element nicht "String-like."

Ein weiterer ungewöhnlicher Ansatz, der für heterogene und homogene Listen von ganzen Zahlen arbeitet:

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(',')]

Eine einfache rekursive Methode reduce von functools und den add Operator auf Listen:

>>> from functools import reduce
>>> from operator import add
>>> flatten = lambda lst: [lst] if type(lst) is int else reduce(add, [flatten(ele) for ele in lst])
>>> flatten(l)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Die Funktion flatten nimmt in lst als Parameter. Es Schleifen alle Elemente lst bis ganzen Zahlen erreicht (auch int ändern float, str usw. für andere Datentypen), die auf den Rückgabewert der äußersten Rekursion hinzugefügt werden.

Rekursion, im Gegensatz zu Methoden wie for Schleifen und Monaden, ist, dass es ist eine allgemeine Lösung nicht von der Liste Tiefe begrenzt . Zum Beispiel kann eine Liste mit einer Tiefe von 5 auf die gleiche Weise wie l abgeflacht werden:

>>> l2 = [[3, [1, 2], [[[6], 5], 4, 0], 7, [[8]], [9, 10]]]
>>> flatten(l2)
[3, 1, 2, 6, 5, 4, 0, 7, 8, 9, 10]
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top