it-swarm.com.de

Reduzieren Sie eine unregelmäßige Liste von Listen

Ja, ich weiß, dass dieses Thema schon einmal behandelt wurde ( hier , hier , hier , hier ), aber soweit ich weiß, alle Lösungen, außer einer, scheitern an einer Liste wie dieser:

L = [[[1, 2, 3], [4, 5]], 6]

Wo die gewünschte Ausgabe ist

[1, 2, 3, 4, 5, 6]

Oder vielleicht noch besser, ein Iterator. Die einzige Lösung, die ich für eine willkürliche Verschachtelung gesehen habe, ist gefunden in dieser Frage :

def flatten(x):
    result = []
    for el in x:
        if hasattr(el, "__iter__") and not isinstance(el, basestring):
            result.extend(flatten(el))
        else:
            result.append(el)
    return result

flatten(L)

Ist das das beste Modell? Habe ich etwas übersehen? Irgendwelche Probleme?

374
telliott99

Die Verwendung von Generatorfunktionen kann Ihr Beispiel etwas lesbarer machen und möglicherweise die Leistung steigern.

Python 2

def flatten(l):
    for el in l:
        if isinstance(el, collections.Iterable) and not isinstance(el, basestring):
            for sub in flatten(el):
                yield sub
        else:
            yield el

Ich habe das Iterable ABC in 2.6 hinzugefügt.

Python 3

In Python 3 ist die Variable basestring nicht mehr vorhanden. Sie können jedoch einen Tuple von str und bytes verwenden, um dort denselben Effekt zu erzielen.

Der yield from-Operator gibt einen Artikel einzeln aus einem Generator zurück. Diese Syntax für das Delegieren an einen Subgenerator wurde in 3.3 hinzugefügt

def flatten(l):
    for el in l:
        if isinstance(el, collections.Iterable) and not isinstance(el, (str, bytes)):
            yield from flatten(el)
        else:
            yield el
331
Cristian

Meine Lösung:

def flatten(x):
    if isinstance(x, collections.Iterable):
        return [a for i in x for a in flatten(i)]
    else:
        return [x]

Etwas prägnanter, aber ziemlich gleich.

46
Josh Lee

Generatorversion der nicht rekursiven Lösung von @unutbu, wie von @Andrew in einem Kommentar angefordert:

def genflat(l, ltypes=collections.Sequence):
    l = list(l)
    i = 0
    while i < len(l):
        while isinstance(l[i], ltypes):
            if not l[i]:
                l.pop(i)
                i -= 1
                break
            else:
                l[i:i + 1] = l[i]
        yield l[i]
        i += 1

Leicht vereinfachte Version dieses Generators:

def genflat(l, ltypes=collections.Sequence):
    l = list(l)
    while l:
        while l and isinstance(l[0], ltypes):
            l[0:1] = l[0]
        if l: yield l.pop(0)
33
Alex Martelli

Generator mit Rekursion und Duck-Typisierung (aktualisiert für Python 3):

def flatten(L):
    for item in L:
        try:
            yield from flatten(item)
        except TypeError:
            yield item

list(flatten([[[1, 2, 3], [4, 5]], 6]))
>>>[1, 2, 3, 4, 5, 6]
32
dansalmo

Diese Version von flatten vermeidet das Rekursionslimit von Python (und arbeitet daher mit beliebig tiefen, geschachtelten Iterables). Es ist ein Generator, der Strings und beliebige iterable (auch unendliche) verarbeiten kann.

import itertools as IT
import collections

def flatten(iterable, ltypes=collections.Iterable):
    remainder = iter(iterable)
    while True:
        first = next(remainder)
        if isinstance(first, ltypes) and not isinstance(first, basestring):
            remainder = IT.chain(first, remainder)
        else:
            yield first

Hier einige Beispiele, die seine Verwendung demonstrieren:

print(list(IT.islice(flatten(IT.repeat(1)),10)))
# [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

print(list(IT.islice(flatten(IT.chain(IT.repeat(2,3),
                                       {10,20,30},
                                       'foo bar'.split(),
                                       IT.repeat(1),)),10)))
# [2, 2, 2, 10, 20, 30, 'foo', 'bar', 1, 1]

print(list(flatten([[1,2,[3,4]]])))
# [1, 2, 3, 4]

seq = ([[chr(i),chr(i-32)] for i in xrange(ord('a'), ord('z')+1)] + range(0,9))
print(list(flatten(seq)))
# ['a', 'A', 'b', 'B', 'c', 'C', 'd', 'D', 'e', 'E', 'f', 'F', 'g', 'G', 'h', 'H',
# 'i', 'I', 'j', 'J', 'k', 'K', 'l', 'L', 'm', 'M', 'n', 'N', 'o', 'O', 'p', 'P',
# 'q', 'Q', 'r', 'R', 's', 'S', 't', 'T', 'u', 'U', 'v', 'V', 'w', 'W', 'x', 'X',
# 'y', 'Y', 'z', 'Z', 0, 1, 2, 3, 4, 5, 6, 7, 8]

Obwohl flatten unendliche Generatoren verarbeiten kann, kann es keine unendliche Verschachtelung verarbeiten:

def infinitely_nested():
    while True:
        yield IT.chain(infinitely_nested(), IT.repeat(1))

print(list(IT.islice(flatten(infinitely_nested()), 10)))
# hangs
25
unutbu

Hier ist meine funktionale Version der rekursiven Abflachung, die sowohl Tupel als auch Listen behandelt und Sie beliebige Positionsargumente einbringen kann. Gibt einen Generator zurück, der die gesamte Sequenz in der Reihenfolge arg nach arg erzeugt:

flatten = lambda *n: (e for a in n
    for e in (flatten(*a) if isinstance(a, (Tuple, list)) else (a,)))

Verwendungszweck:

l1 = ['a', ['b', ('c', 'd')]]
l2 = [0, 1, (2, 3), [[4, 5, (6, 7, (8,), [9]), 10]], (11,)]
print list(flatten(l1, -2, -1, l2))
['a', 'b', 'c', 'd', -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
21
samplebias

Hier ist eine weitere Antwort, die noch interessanter ist ...

import re

def Flatten(TheList):
    a = str(TheList)
    b,crap = re.subn(r'[\[,\]]', ' ', a)
    c = b.split()
    d = [int(x) for x in c]

    return(d)

Im Grunde konvertiert es die verschachtelte Liste in einen String, entfernt die verschachtelte Syntax mit einem Regex und konvertiert das Ergebnis anschließend wieder in eine (reduzierte) Liste.

13
clay
def flatten(xs):
    res = []
    def loop(ys):
        for i in ys:
            if isinstance(i, list):
                loop(i)
            else:
                res.append(i)
    loop(xs)
    return res
10
kev

Sie können deepflatten aus dem Drittanbieter-Paket iteration_utilities verwenden:

>>> from iteration_utilities import deepflatten
>>> L = [[[1, 2, 3], [4, 5]], 6]
>>> list(deepflatten(L))
[1, 2, 3, 4, 5, 6]

>>> list(deepflatten(L, types=list))  # only flatten "inner" lists
[1, 2, 3, 4, 5, 6]

Es handelt sich um einen Iterator, also müssen Sie ihn iterieren (z. B. indem Sie ihn mit list umschließen oder in einer Schleife verwenden). Intern verwendet es einen iterativen Ansatz anstelle eines rekursiven Ansatzes und wird als C-Erweiterung geschrieben, sodass es schneller als reine Python-Ansätze sein kann:

>>> %timeit list(deepflatten(L))
12.6 µs ± 298 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
>>> %timeit list(deepflatten(L, types=list))
8.7 µs ± 139 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

>>> %timeit list(flatten(L))   # Cristian - Python 3.x approach from https://stackoverflow.com/a/2158532/5393381
86.4 µs ± 4.42 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

>>> %timeit list(flatten(L))   # Josh Lee - https://stackoverflow.com/a/2158522/5393381
107 µs ± 2.99 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

>>> %timeit list(genflat(L, list))  # Alex Martelli - https://stackoverflow.com/a/2159079/5393381
23.1 µs ± 710 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

Ich bin der Autor der iteration_utilities-Bibliothek.

7
MSeifert

Es hat Spaß gemacht, eine Funktion zu erstellen, mit der die unregelmäßige Liste in Python abgeflacht werden könnte, aber dafür ist Python natürlich gedacht (um das Programmieren zu erleichtern). Der folgende Generator funktioniert ziemlich gut mit einigen Vorbehalten:

def flatten(iterable):
    try:
        for item in iterable:
            yield from flatten(item)
    except TypeError:
        yield iterable

Dadurch werden Datentypen reduziert, die Sie möglicherweise alleine lassen möchten (wie bytearray, bytes und str-Objekte). Außerdem beruht der Code auf der Tatsache, dass das Anfordern eines Iterators von einem nicht iterierbaren Element eine TypeError auslöst.

>>> L = [[[1, 2, 3], [4, 5]], 6]
>>> def flatten(iterable):
    try:
        for item in iterable:
            yield from flatten(item)
    except TypeError:
        yield iterable


>>> list(flatten(L))
[1, 2, 3, 4, 5, 6]
>>>

Bearbeiten:

Ich stimme der vorherigen Implementierung nicht zu. Das Problem ist, dass Sie nicht in der Lage sein sollten, etwas abzuflachen, das nicht iterierbar ist. Es ist verwirrend und vermittelt den falschen Eindruck des Arguments.

>>> list(flatten(123))
[123]
>>>

Der folgende Generator ist fast derselbe wie der erste, hat jedoch nicht das Problem, zu versuchen, ein nicht iterierbares Objekt zu reduzieren. Es scheitert wie erwartet, wenn ein unangemessenes Argument vorgebracht wird.

def flatten(iterable):
    for item in iterable:
        try:
            yield from flatten(item)
        except TypeError:
            yield item

Das Testen des Generators funktioniert gut mit der bereitgestellten Liste. Der neue Code führt jedoch zu einer TypeError, wenn ihm ein nicht iterierbares Objekt übergeben wird. Nachfolgend werden Beispiele für das neue Verhalten gezeigt.

>>> L = [[[1, 2, 3], [4, 5]], 6]
>>> list(flatten(L))
[1, 2, 3, 4, 5, 6]
>>> list(flatten(123))
Traceback (most recent call last):
  File "<pyshell#32>", line 1, in <module>
    list(flatten(123))
  File "<pyshell#27>", line 2, in flatten
    for item in iterable:
TypeError: 'int' object is not iterable
>>>
6
Noctis Skytower

Obwohl eine elegante und sehr pythonische Antwort ausgewählt wurde, würde ich meine Lösung nur für die Überprüfung präsentieren:

def flat(l):
    ret = []
    for i in l:
        if isinstance(i, list) or isinstance(i, Tuple):
            ret.extend(flat(i))
        else:
            ret.append(i)
    return ret

Bitte sagen Sie, wie gut oder wie schlecht dieser Code ist.

4
Xolve

Hier ist eine einfache Funktion, die Listen beliebiger Tiefe flacher macht. Keine Rekursion, um Stapelüberlauf zu vermeiden.

from copy import deepcopy

def flatten_list(nested_list):
    """Flatten an arbitrarily nested list, without recursion (to avoid
    stack overflows). Returns a new list, the original list is unchanged.

    >> list(flatten_list([1, 2, 3, [4], [], [[[[[[[[[5]]]]]]]]]]))
    [1, 2, 3, 4, 5]
    >> list(flatten_list([[1, 2], 3]))
    [1, 2, 3]

    """
    nested_list = deepcopy(nested_list)

    while nested_list:
        sublist = nested_list.pop(0)

        if isinstance(sublist, list):
            nested_list = sublist + nested_list
        else:
            yield sublist
4
Wilfred Hughes

Ich bevorzuge einfache Antworten. Keine Generatoren. Keine Rekursions- oder Rekursionslimits. Nur Iteration:

def flatten(TheList):
    listIsNested = True

    while listIsNested:                 #outer loop
        keepChecking = False
        Temp = []

        for element in TheList:         #inner loop
            if isinstance(element,list):
                Temp.extend(element)
                keepChecking = True
            else:
                Temp.append(element)

        listIsNested = keepChecking     #determine if outer loop exits
        TheList = Temp[:]

    return TheList

Dies funktioniert mit zwei Listen: einer inneren for-Schleife und einer äußeren while-Schleife. 

Die innere for-Schleife durchläuft die Liste. Wenn es ein Listenelement findet, verwendet es (1) list.extend (), um die Ebene der Verschachtelung von Teil 1 zu glätten, und (2) schaltet die OptionCheck auf True. Keepchecking wird verwendet, um die äußere While-Schleife zu steuern. Wenn die äußere Schleife auf wahr gesetzt wird, wird die innere Schleife für einen weiteren Durchlauf ausgelöst. 

Diese Pässe passieren so lange, bis keine verschachtelten Listen mehr gefunden werden. Wenn schließlich ein Pass ausgeführt wird, bei dem keiner gefunden wird, wird keepChecking nie auf true gesetzt, was bedeutet, dass listIsNested auf false bleibt und die äußere while-Schleife beendet wird. 

Die abgeflachte Liste wird dann zurückgegeben.

Testlauf 

flatten([1,2,3,4,[100,200,300,[1000,2000,3000]]])

[1, 2, 3, 4, 100, 200, 300, 1000, 2000, 3000]

4
clay

Hier ist die compiler.ast.flatten-Implementierung in 2.7.5:

def flatten(seq):
    l = []
    for elt in seq:
        t = type(elt)
        if t is Tuple or t is list:
            for elt2 in flatten(elt):
                l.append(elt2)
        else:
            l.append(elt)
    return l

Es gibt bessere, schnellere Methoden (Wenn Sie hier angekommen sind, haben Sie sie bereits gesehen)

Beachten Sie auch:

Veraltet seit Version 2.6: Das Compilerpaket wurde in Python 3 entfernt.

2
pradyunsg

Ich habe nicht alle hier verfügbaren Antworten durchgegangen, aber hier ist ein One-Liner, den ich aus LISPs Weg der First- und Rest-List-Verarbeitung übernommen habe 

def flatten(l): return flatten(l[0]) + (flatten(l[1:]) if len(l) > 1 else []) if type(l) is list else [l]

hier ist ein einfacher und ein nicht so einfacher Fall -

>>> flatten([1,[2,3],4])
[1, 2, 3, 4]

>>> flatten([1, [2, 3], 4, [5, [6, {'name': 'some_name', 'age':30}, 7]], [8, 9, [10, [11, [12, [13, {'some', 'set'}, 14, [15, 'some_string'], 16], 17, 18], 19], 20], 21, 22, [23, 24], 25], 26, 27, 28, 29, 30])
[1, 2, 3, 4, 5, 6, {'age': 30, 'name': 'some_name'}, 7, 8, 9, 10, 11, 12, 13, set(['set', 'some']), 14, 15, 'some_string', 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
>>> 
2
Shreyas

Wenn Sie versuchen, eine solche Frage zu beantworten, müssen Sie wirklich die Einschränkungen des Codes angeben, den Sie als Lösung vorschlagen. Wenn es nur um Performances ginge, würde es mir nicht allzu viel ausmachen, aber die meisten als Lösung vorgeschlagenen Codes (einschließlich der akzeptierten Antwort) können keine Liste mit einer Tiefe von mehr als 1000 reduzieren.

Wenn ich sage die meisten Codes, meine ich alle Codes, die irgendeine Form der Rekursion verwenden (oder eine Standardbibliotheksfunktion aufrufen, die rekursiv ist). Alle diese Codes schlagen fehl, da der (Aufruf-) Stapel für jeden durchgeführten rekursiven Aufruf um eine Einheit wächst und der (Standard-) python Aufruf-Stapel eine Größe von 1000 hat.

Wenn Sie mit der Aufrufliste nicht allzu vertraut sind, hilft möglicherweise Folgendes (ansonsten können Sie einfach zur Implementierung scrollen ).

Call-Stack-Größe und rekursive Programmierung (Dungeon-Analogie)

Finde den Schatz und gehe

Stellen Sie sich vor, Sie betreten einen riesigen Kerker mit nummerierten Räumen und suchen nach einem Schatz. Sie kennen den Ort nicht, aber Sie haben einige Hinweise , wie Sie den Schatz finden können. Jede Indikation ist ein Rätsel (der Schwierigkeitsgrad variiert, aber Sie können nicht vorhersagen, wie schwer sie sein werden). Sie beschließen, ein wenig über eine Strategie nachzudenken, um Zeit zu sparen, und machen zwei Beobachtungen:

  1. Es ist schwierig (lange), den Schatz zu finden, da Sie (möglicherweise schwierige) Rätsel lösen müssen, um dorthin zu gelangen.
  2. Sobald der Schatz gefunden ist, kann es leicht sein, zum Eingang zurückzukehren. Sie müssen nur den gleichen Pfad in die andere Richtung verwenden (dies erfordert jedoch ein wenig Gedächtnis, um sich an Ihren Pfad zu erinnern).

Beim Betreten des Verlieses fällt Ihnen hier ein kleines Notizbuch auf. Sie beschließen, jeden Raum, den Sie verlassen, zu notieren, nachdem Sie ein Rätsel gelöst haben (wenn Sie einen neuen Raum betreten). Auf diese Weise können Sie zum Eingang zurückkehren. Das ist eine geniale Idee, Sie geben nicht einmal einen Cent aus setzen Ihre Strategie um.

Sie betreten den Dungeon und lösen mit großem Erfolg die ersten 1001 Rätsel, aber hier kommt etwas, das Sie nicht geplant hatten, Sie haben keinen Platz mehr in dem Notizbuch, das Sie ausgeliehen haben. Sie beschließen, aufzugeben Ihre Suche, da Sie es vorziehen, den Schatz nicht zu haben, als für immer im Kerker verloren zu sein (das sieht in der Tat klug aus).

Ausführen eines rekursiven Programms

Im Grunde ist es genau das Gleiche wie das Finden des Schatzes. Der Dungeon ist das Gedächtnis des Computers, dein Ziel ist jetzt nicht, einen Schatz zu finden, sondern eine Funktion zu berechnen (find f (x) für ein gegebenes x). Die Angaben sind lediglich Unterprogramme, die Ihnen beim Lösen von f (x) helfen. Ihre Strategie ist die gleiche wie die Aufrufstapel Strategie, das Notizbuch ist der Stapel, die Räume sind die Absenderadressen der Funktionen:

x = ["over here", "am", "I"]
y = sorted(x) # You're about to enter a room named `sorted`, note down the current room address here so you can return back: 0x4004f4 (that room address looks weird)
# Seems like you went back from your quest using the return address 0x4004f4
# Let's see what you've collected 
print(' '.join(y))

Das Problem, auf das Sie im Dungeon gestoßen sind, ist hier dasselbe. Der Aufrufstapel hat eine endliche Größe (hier 1000). Wenn Sie also zu viele Funktionen eingeben, ohne zurückzugelangen, füllen Sie den Aufrufstapel und es tritt ein Fehler auf, der aussieht mögen "Lieber Abenteurer, es tut mir sehr leid, aber dein Notizbuch ist voll.": RecursionError: maximum recursion depth exceeded. Beachten Sie, dass Sie keine Rekursion benötigen, um den Aufrufstapel zu füllen. Es ist jedoch sehr unwahrscheinlich, dass ein nicht rekursives Programm 1000 aufruft, ohne jemals zurückzukehren. Es ist auch wichtig zu verstehen, dass nach der Rückkehr von einer Funktion der Aufrufstapel von der verwendeten Adresse befreit wird (daher werden der Name "Stapel" und die Rücksprungadresse vor der Eingabe einer Funktion eingegeben und bei der Rückkehr herausgezogen). Im Sonderfall einer einfachen Rekursion (eine Funktion f, die sich immer wieder selbst aufruft) geben Sie f immer wieder ein, bis die Berechnung abgeschlossen ist (bis der Schatz ist gefunden) und kehre von f zurück, bis du an die Stelle zurückgehst, an der du f angerufen hast. Der Aufrufstapel wird niemals von irgendetwas befreit, bis zu dem Ende, an dem er nacheinander von allen Rücksprungadressen befreit wird.

Wie vermeide ich dieses Problem?

Das ist eigentlich ganz einfach: "Benutze keine Rekursion, wenn du nicht weißt, wie tief sie gehen kann". Dies ist nicht immer der Fall, da in einigen Fällen Tail Call-Rekursion kann optimiert werden (TCO) . In Python ist dies jedoch nicht der Fall, und selbst eine "gut geschriebene" rekursive Funktion optimiert die Stapelverwendung nicht . Zu dieser Frage gibt es einen interessanten Beitrag von Guido: Tail Recursion Elimination .

Es gibt eine Technik, mit der Sie jede rekursive Funktion iterativ machen können. Diese Technik könnten wir nennen. Bringen Sie Ihr eigenes Notizbuch mit =. In unserem speziellen Fall untersuchen wir beispielsweise einfach eine Liste. Das Betreten eines Raums entspricht dem Betreten einer Unterliste. Die Frage, die Sie sich stellen sollten, ist Wie kann ich von einer Liste zur übergeordneten Liste zurückkehren ? Die Antwort ist nicht so komplex. Wiederholen Sie Folgendes, bis das stack leer ist:

  1. Verschieben Sie die aktuelle Liste address und index in ein stack, wenn Sie eine neue Unterliste eingeben (beachten Sie, dass eine Listenadresse + Index auch eine Adresse ist, daher verwenden wir nur die exakt gleiche Technik, die der Aufrufstapel verwendet);
  2. jedes Mal, wenn ein Element gefunden wird, yield es (oder fügen Sie sie in eine Liste);
  3. sobald eine Liste vollständig durchsucht wurde, kehren Sie mit den Tasten stack return address (und index) zur übergeordneten Liste zurück.

Beachten Sie außerdem, dass dies einem DFS in einem Baum entspricht, in dem einige Knoten Unterlisten A = [1, 2] und einige einfache Elemente sind: 0, 1, 2, 3, 4 (für L = [0, [1,2], 3, 4]). Der Baum sieht so aus:

                    L
                    |
           -------------------
           |     |     |     |
           0   --A--   3     4
               |   |
               1   2

Die DFS-Durchlauf-Vorreihenfolge lautet: L, 0, A, 1, 2, 3, 4. Denken Sie daran, dass Sie zum Implementieren eines iterativen DFS auch einen Stapel "benötigen". Die Implementierung, die ich zuvor vorgeschlagen habe, hat die folgenden Zustände (für das stack und das flat_list):

init.:  stack=[(L, 0)]
**0**:  stack=[(L, 0)],         flat_list=[0]
**A**:  stack=[(L, 1), (A, 0)], flat_list=[0]
**1**:  stack=[(L, 1), (A, 0)], flat_list=[0, 1]
**2**:  stack=[(L, 1), (A, 1)], flat_list=[0, 1, 2]
**3**:  stack=[(L, 2)],         flat_list=[0, 1, 2, 3]
**3**:  stack=[(L, 3)],         flat_list=[0, 1, 2, 3, 4]
return: stack=[],               flat_list=[0, 1, 2, 3, 4]

In diesem Beispiel beträgt die maximale Stapelgröße 2, da die Eingabeliste (und damit der Baum) die Tiefe 2 hat.

Implementierung

Für die Implementierung können Sie in python ein wenig vereinfachen, indem Sie Iteratoren anstelle von einfachen Listen verwenden. Verweise auf die (Unter-) Iteratoren werden zum Speichern von Unterlisten verwendet, die Adressen zurückgeben (anstatt sowohl die Listenadresse als auch den Index zu haben). Dies ist kein großer Unterschied, aber meiner Meinung nach ist dies lesbarer (und auch etwas schneller):

def flatten(iterable):
    return list(items_from(iterable))

def items_from(iterable):
    cursor_stack = [iter(iterable)]
    while cursor_stack:
        sub_iterable = cursor_stack[-1]
        try:
            item = next(sub_iterable)
        except StopIteration:   # post-order
            cursor_stack.pop()
            continue
        if is_list_like(item):  # pre-order
            cursor_stack.append(iter(item))
        Elif item is not None:
            yield item          # in-order

def is_list_like(item):
    return isinstance(item, list)

Beachten Sie auch, dass in is_list_like ich isinstance(item, list) habe, das geändert werden kann, um mehr Eingabetypen zu verarbeiten. Hier wollte ich nur die einfachste Version haben, in der (iterable) nur eine Liste ist. Das können Sie aber auch tun:

def is_list_like(item):
    try:
        iter(item)
        return not isinstance(item, str)  # strings are not lists (hmm...) 
    except TypeError:
        return False

Dies betrachtet Zeichenfolgen als "einfache Elemente" und daher gibt flatten_iter([["test", "a"], "b])["test", "a", "b"] und nicht ["t", "e", "s", "t", "a", "b"] zurück. Beachten Sie, dass in diesem Fall iter(item) für jedes Element zweimal aufgerufen wird. Nehmen wir an, es sei eine Übung für den Leser, diese Funktion sauberer zu gestalten.

Testen und Anmerkungen zu anderen Implementierungen

Denken Sie am Ende daran, dass Sie mit print(L) keine unendlich verschachtelte Liste L drucken können, da intern rekursive Aufrufe von __repr__ (RecursionError: maximum recursion depth exceeded while getting the repr of an object). Aus demselben Grund schlagen Lösungen für flatten, an denen str beteiligt ist, mit derselben Fehlermeldung fehl.

Wenn Sie Ihre Lösung testen müssen, können Sie mit dieser Funktion eine einfache verschachtelte Liste erstellen:

def build_deep_list(depth):
    """Returns a list of the form $l_{depth} = [depth-1, l_{depth-1}]$
    with $depth > 1$ and $l_0 = [0]$.
    """
    sub_list = [0]
    for d in range(1, depth):
        sub_list = [d, sub_list]
    return sub_list

Was gibt: build_deep_list(5) >>> [4, [3, [2, [1, [0]]]]].

2
cglacet

Ich bin überrascht, dass niemand daran gedacht hat. Verdammte Rekursion Ich bekomme nicht die rekursiven Antworten, die die fortgeschrittenen Leute hier gemacht haben. Jedenfalls ist hier mein Versuch dazu. Achtung, es ist sehr spezifisch für den Anwendungsfall des OP

import re

L = [[[1, 2, 3], [4, 5]], 6]
flattened_list = re.sub("[\[\]]", "", str(L)).replace(" ", "").split(",")
new_list = list(map(int, flattened_list))
print(new_list)

ausgabe:

[1, 2, 3, 4, 5, 6]
2
Zion

Der einfachste Weg ist die Verwendung der Bibliothek morph mit pip install morph.

Der Code lautet:

import morph

list = [[[1, 2, 3], [4, 5]], 6]
flattened_list = morph.flatten(list)  # returns [1, 2, 3, 4, 5, 6]
1
YPCrumble

total hacky aber ich denke es würde funktionieren (abhängig von deinem data_type)

flat_list = ast.literal_eval("[%s]"%re.sub("[\[\]]","",str(the_list)))
1
Joran Beasley

Hier ist ein weiterer py2-Ansatz. Ich bin mir nicht sicher, ob es der schnellste oder der eleganteste oder sicherste ist.

from collections import Iterable
from itertools import imap, repeat, chain


def flat(seqs, ignore=(int, long, float, basestring)):
    return repeat(seqs, 1) if any(imap(isinstance, repeat(seqs), ignore)) or not isinstance(seqs, Iterable) else chain.from_iterable(imap(flat, seqs))

Es kann jeden bestimmten (oder abgeleiteten) Typ ignorieren, den Sie möchten, es wird ein Iterator zurückgegeben. Sie können ihn also in einen beliebigen Container wie list, Tuple, dict konvertieren oder ihn einfach verbrauchen, um den Speicherbedarf zu verbessern oder zu verringern Es kann anfängliche, nicht iterierbare Objekte wie Int ...

Beachten Sie, dass das meiste der schweren Anhebungen in C ausgeführt wird, da soweit ich weiß, wie Itertools implementiert werden. Während es rekursiv ist, ist AFAIK jedoch nicht an die Python-Rekursionstiefe gebunden, da die Funktionsaufrufe in C stattfinden, obwohl dies der Fall ist Das bedeutet nicht, dass Sie an den Arbeitsspeicher gebunden sind, insbesondere in OS X, wo die Stack-Größe derzeit ein hartes Limit hat (OS X Mavericks) ...

es gibt einen etwas schnelleren Ansatz, aber weniger tragbare Methode. Verwenden Sie diese Methode nur, wenn Sie davon ausgehen können, dass die Basiselemente der Eingabe explizit bestimmt werden können. Andernfalls erhalten Sie eine unendliche Rekursion und OS X mit seiner begrenzten Stapelgröße einen Segmentierungsfehler ziemlich schnell werfen ...

def flat(seqs, ignore={int, long, float, str, unicode}):
    return repeat(seqs, 1) if type(seqs) in ignore or not isinstance(seqs, Iterable) else chain.from_iterable(imap(flat, seqs))

hier verwenden wir Sets, um nach dem Typ zu suchen, so dass O(1) vs. O (Anzahl der Typen) erforderlich ist, um zu prüfen, ob ein Element ignoriert werden soll oder nicht, obwohl natürlich jeder Wert mit dem abgeleiteten Typ des angegebenen Typs ist Ignorierte Typen schlagen fehl, deshalb verwenden sie str, unicode, verwenden Sie sie also mit Vorsicht ...

tests:

import random

def test_flat(test_size=2000):
    def increase_depth(value, depth=1):
        for func in xrange(depth):
            value = repeat(value, 1)
        return value

    def random_sub_chaining(nested_values):
        for values in nested_values:
            yield chain((values,), chain.from_iterable(imap(next, repeat(nested_values, random.randint(1, 10)))))

    expected_values = Zip(xrange(test_size), imap(str, xrange(test_size)))
    nested_values = random_sub_chaining((increase_depth(value, depth) for depth, value in enumerate(expected_values)))
    assert not any(imap(cmp, chain.from_iterable(expected_values), flat(chain(((),), nested_values, ((),)))))

>>> test_flat()
>>> list(flat([[[1, 2, 3], [4, 5]], 6]))
[1, 2, 3, 4, 5, 6]
>>>  

$ uname -a
Darwin Samys-MacBook-Pro.local 13.3.0 Darwin Kernel Version 13.3.0: Tue Jun  3 21:27:35 PDT 2014; root:xnu-2422.110.17~1/RELEASE_X86_64 x86_64
$ python --version
Python 2.7.5
1
Samy Vilar

Mir ist bewusst, dass es bereits viele großartige Antworten gibt, aber ich wollte eine Antwort hinzufügen, die die funktionale Programmiermethode zur Lösung der Frage verwendet. In dieser Antwort verwende ich die doppelte Rekursion:

def flatten_list(seq):
    if not seq:
        return []
    Elif isinstance(seq[0],list):
        return (flatten_list(seq[0])+flatten_list(seq[1:]))
    else:
        return [seq[0]]+flatten_list(seq[1:])

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

ausgabe:

[1, 2, 3, 4, 5, 6, 7]
1
Leo wahyd

Ich bin mir nicht sicher, ob dies schneller oder effektiver ist, aber ich mache das:

def flatten(lst):
    return eval('[' + str(lst).replace('[', '').replace(']', '') + ']')

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

Die Funktion flatten wandelt die Liste in einen String um, entfernt all der eckigen Klammern, fügt eckige Klammern wieder an den Enden hinzu und wandelt sie zurück in eine Liste. 

Wenn Sie jedoch wüssten, dass Sie in Ihrer Liste eckige Klammern in Zeichenfolgen wie [[1, 2], "[3, 4] and [5]"] enthalten, müssen Sie etwas anderes tun.

1
diligar

itertools.chain verwenden:

import itertools
from collections import Iterable

def list_flatten(lst):
    flat_lst = []
    for item in itertools.chain(lst):
        if isinstance(item, Iterable):
            item = list_flatten(item)
            flat_lst.extend(item)
        else:
            flat_lst.append(item)
    return flat_lst

Oder ohne Verkettung:

def flatten(q, final):
    if not q:
        return
    if isinstance(q, list):
        if not isinstance(q[0], list):
            final.append(q[0])
        else:
            flatten(q[0], final)
        flatten(q[1:], final)
    else:
        final.append(q)
1
Saksham Varma

Verwenden Sie einfach eine funcy Bibliothek: pip install funcy

import funcy


funcy.flatten([[[[1, 1], 1], 2], 3]) # returns generator
funcy.lflatten([[[[1, 1], 1], 2], 3]) # returns list

1
ADR

Ich habe rekursiv verwendet, umverschachtelte Liste mit beliebiger Tiefe zu lösen

def combine_nlist(nlist,init=0,combiner=lambda x,y: x+y):
    '''
    apply function: combiner to a nested list element by element(treated as flatten list)
    '''
    current_value=init
    for each_item in nlist:
        if isinstance(each_item,list):
            current_value =combine_nlist(each_item,current_value,combiner)
        else:
            current_value = combiner(current_value,each_item)
    return current_value

Nachdem ich also die Funktion „kombinieren_nlist“ definiert habe, ist es einfach, diese Funktion zum Flaten zu verwenden. Oder Sie kombinieren es zu einer Funktion. Ich mag meine Lösung, weil sie auf jede verschachtelte Liste angewendet werden kann.

def flatten_nlist(nlist):
    return combine_nlist(nlist,[],lambda x,y:x+[y])

ergebnis

In [379]: flatten_nlist([1,2,3,[4,5],[6],[[[7],8],9],10])
Out[379]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
1
freeyoung

Iterative Lösung mit Python 3

Diese Lösung funktioniert möglicherweise mit allen Objekten außer str und Byte.

from collections import Iterable
from collections import Iterator


def flat_iter(obj):
    stack = [obj]
    while stack:
        element = stack.pop()
        if element and isinstance(element, Iterator):
            stack.append(element)
            try:
                stack.append(next(element))
            except StopIteration:
                stack.pop()
        Elif isinstance(element, Iterable) and not isinstance(element, (str, bytes)):
            stack.append(iter(element))
        else:
            yield element


tree_list = [[(1,2,3),(4,5,6, (7,8, 'next element is 5')), (5,6), [[[3,4,5],'foo1'],'foo2'],'foo3']]

not_iterable = 10

it1 = flat_iter(tree_list)
it2 = flat_iter(not_iterable)

print(list(it1))
print(list(it2))

[1, 2, 3, 4, 5, 6, 7, 8, 'nächstes Element ist 5', 5, 6, 3, 4, 5, 'foo1', 'foo2', 'foo3']

[10]

0
DeaD_EyE

dies könnte eine alte Frage sein, auf die ich gerne einen Schuss werfen möchte.

Ich bin ein dummer Kerl, also gebe ich eine "dumme" Lösung. All diese Rekursion schmerzt mein Gehirn.

flattened_list  = []
nested_list =[[[1, 2, 3], [4, 5]], 6]

def flatten(nested_list, container):
    for item in nested_list:
        if isintance(item, list):
            flatten(item)
        else:
            container.append(i)

>>> flatten(nested_list, flattened_list)
>>> flattened_list
[1, 2, 3, 4, 5, 6]

Ich verstehe, dass es einen Nebeneffekt hat, aber das kann ich nach meinem besten Verständnis der Rekursion gehen

0
halcyonjuly7

Ich sehe nichts dergleichen hier und bin gerade von einer geschlossenen Frage zum selben Thema hierher gekommen, aber warum nicht einfach so etwas tun (wenn Sie den Typ der Liste kennen, die Sie teilen möchten):

>>> a = [1, 2, 3, 5, 10, [1, 25, 11, [1, 0]]]    
>>> g = str(a).replace('[', '').replace(']', '')    
>>> b = [int(x) for x in g.split(',') if x.strip()]

Sie müssten die Art der Elemente kennen, aber ich denke, dass dies verallgemeinert werden kann, und in Bezug auf die Geschwindigkeit denke ich, dass es schneller wäre.

0
Bogdan

Keine Rekursion oder verschachtelte Schleifen. Ein paar Zeilen. Gut formatiert und gut lesbar:

def flatten_deep(arr: list):
""" Flattens arbitrarily-nested list `arr` into single-dimensional. """

while arr:
    if isinstance(arr[0], list):  # Checks whether first element is a list
        arr = arr[0] + arr[1:]  # If so, flattens that first element one level
    else:
        yield arr.pop(0)  # Otherwise yield as part of the flat array

flatten_deep(L)

Aus meinem eigenen Code unter https://github.com/jorgeorpinel/flatten_nested_lists/blob/master/flatten.py

0
Jorge Orpinel

Dies ist eine einfache Implementierung von flatten on python2

flatten=lambda l: reduce(lambda x,y:x+y,map(flatten,l),[]) if isinstance(l,list) else [l]

test=[[1,2,3,[3,4,5],[6,7,[8,9,[10,[11,[12,13,14]]]]]],]
print flatten(test)

#output [1, 2, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
0
Statham

Ohne eine Bibliothek zu verwenden:

def flat(l):
    def _flat(l, r):    
        if type(l) is not list:
            r.append(l)
        else:
            for i in l:
                r = r + flat(i)
        return r
    return _flat(l, [])



# example
test = [[1], [[2]], [3], [['a','b','c'] , [['z','x','y']], ['d','f','g']], 4]    
print flat(test) # prints [1, 2, 3, 'a', 'b', 'c', 'z', 'x', 'y', 'd', 'f', 'g', 4]
0
alfasin

Dies reduziert eine Liste oder ein Wörterbuch (oder eine Liste von Listen oder Wörterbüchern von Wörterbüchern usw.). Es wird davon ausgegangen, dass die Werte Zeichenfolgen sind, und es wird eine Zeichenfolge erstellt, die jedes Element mit einem Trennzeichenargument verkettet. Wenn Sie möchten, können Sie das Ergebnis mit dem Trennzeichen in ein Listenobjekt aufteilen. Die Rekursion wird verwendet, wenn der nächste Wert eine Liste oder eine Zeichenfolge ist. Verwenden Sie das Schlüsselargument, um zu bestimmen, ob Sie die Schlüssel oder die Werte (setzen Sie den Schlüssel auf "false") aus dem Wörterbuchobjekt. 

def flatten_obj(n_obj, key=True, my_sep=''):
    my_string = ''
    if type(n_obj) == list:
        for val in n_obj:
            my_sep_setter = my_sep if my_string != '' else ''
            if type(val) == list or type(val) == dict:
                my_string += my_sep_setter + flatten_obj(val, key, my_sep)
            else:
                my_string += my_sep_setter + val
    Elif type(n_obj) == dict:
        for k, v in n_obj.items():
            my_sep_setter = my_sep if my_string != '' else ''
            d_val = k if key else v
            if type(v) == list or type(v) == dict:
                my_string += my_sep_setter + flatten_obj(v, key, my_sep)
            else:
                my_string += my_sep_setter + d_val
    Elif type(n_obj) == str:
        my_sep_setter = my_sep if my_string != '' else ''
        my_string += my_sep_setter + n_obj
        return my_string
    return my_string

print(flatten_obj(['just', 'a', ['test', 'to', 'try'], 'right', 'now', ['or', 'later', 'today'],
                [{'dictionary_test': 'test'}, {'dictionary_test_two': 'later_today'}, 'my power is 9000']], my_sep=', ')

ergibt:

nur, ein, test, an, versuchen, richtig, jetzt oder später, heute, dictionary_test, dictionary_test_two, meine Macht liegt bei 9000

0
Matt Farguson

Ich bin neu in Python und komme aus einem LISP-Hintergrund. Das ist, was ich mir ausgedacht habe (schaut euch die var Namen für lulz an):

def flatten(lst):
    if lst:
        car,*cdr=lst
        if isinstance(car,(list,Tuple)):
            if cdr: return flatten(car) + flatten(cdr)
            return flatten(car)
        if cdr: return [car] + flatten(cdr)
        return [car]

Scheint zu funktionieren. Prüfung:

flatten((1,2,3,(4,5,6,(7,8,(((1,2)))))))

kehrt zurück:

[1, 2, 3, 4, 5, 6, 7, 8, 1, 2]
0
Michael Puckett

Python-3

from collections import Iterable

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

def flatten(thing):
    result = []

    if isinstance(thing, Iterable):
        for item in thing:
            result.extend(flatten(item))
    else:
        result.append(thing)

    return result


flat = flatten(L)
print(flat)
0
Fuji Komalan

Wir können auch die 'type'-Funktion von Python verwenden. Beim Durchlaufen der Liste überprüfen wir, ob es sich bei dem Element um eine Liste handelt oder nicht. Wenn nicht, fügen wir es hinzu, sonst erweitern wir es. Hier ist ein Beispielcode - 

l=[1,2,[3,4],5,[6,7,8]]
x=[]
for i in l:
    if type(i) is list:
        x.extend(i)
    else:
        x.append(i)
print x

Ausgabe:

[1, 2, 3, 4, 5, 6, 7, 8]

Weitere Informationen zu append () und extend () finden Sie auf dieser Website: https://docs.python.org/2/tutorial/datastructures.html

0
noobcoder

Ähnlich wie die Antwort von Diligar

weird_list=[[1, 2, 3], [4, 5, 6], [7], [8, 9]]
Nice_list = list(map(int, ''.join([e for e in str(weird_list) if e not in '[ ]']).split(',')))
0

Aus meiner vorherigen Antwort flacht diese Funktion die meisten Fälle ab, die mir einfallen. Ich glaube, das funktioniert bis auf Python 2.3.

def flatten(item, keepcls=(), keepobj=()):
    if not hasattr(item, '__iter__') or isinstance(item, keepcls) or item in keepobj:
        yield item
    else:
        for i in item:
            for j in flatten(i, keepcls, keepobj + (item,)):
                yield j

Kreislisten

>>> list(flatten([1, 2, [...], 3]))
[1, 2, [1, 2, [...], 3], 3]

Tiefe erste Listen

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

Verschachtelte Wiederholungslisten:

>>> list(flatten([[1,2],[1,[1,2]],[1,2]]))
[1, 2, 1, 1, 2, 1, 2]

Listen mit Diktaten (oder anderen Objekten, die nicht abgeflacht werden sollen)

>>> list(flatten([1,2, {'a':1, 'b':2}, 'text'], keepcls=(dict, str)))
[1, 2, {'a': 1, 'b': 2}, 'text']

Irgendwelche iterables

>>> list(flatten((x for x in [1,2, set([3,(4,5),6])])))
[1, 2, 4, 5, 3, 6]

Sie können einige Standardklassen in keepcls beibehalten, um den Aufruf von .__ zu ermöglichen. die Funktion knapper.

0
wihlke

Wenn Sie Rekursion mögen, könnte dies eine interessante Lösung für Sie sein:

def f(E):
    if E==[]: 
        return []
    Elif type(E) != list: 
        return [E]
    else:
        a = f(E[0])
        b = f(E[1:])
        a.extend(b)
        return a

Ich habe das tatsächlich von einem Code aus dem Übungsschema übernommen, den ich vor einiger Zeit geschrieben hatte. 

Genießen!

0
inspectorG4dget

Schamlos aus meiner eigenen Antwort auf eine andere Frage genommen.

Diese Funktion

  • Verwendet nicht isinstance, weil es böse ist und das Eingeben von Enten bricht.
  • Verwendet reduce rekursiv. Es muss eine Antwort mit reduce geben.
  • Arbeitet mit beliebigen verschachtelten Listen, deren Elemente entweder verschachtelte Listen oder nicht verschachtelte Listen von Atomen oder Atomen sind (unter Berücksichtigung der Rekursionsgrenze).
  • Nicht LBYL.
  • Nicht jedoch mit verschachtelten Listen, die Zeichenfolgen als Atome enthalten.

Code unten:

def flattener(left, right):
    try:
        res = reduce(flattener, right, left)
    except TypeError:
        left.append(right)
        res = left
    return res


def flatten(seq):
    return reduce(flattener, seq, [])


>>> nested_list = [0, [1], [[[[2]]]],
                   [3, [], [4, 5]],
                   [6, [7, 8],
                    9, [[[]], 10,
                        []]],
                   11, [], [],
                   [12]]
>>> flatten(nested_list)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
0
Cong Ma