432 Stimmen

Verflachen einer flachen Liste in Python

Gibt es eine einfache Möglichkeit, eine Liste von iterables mit einer Liste Verständnis zu glätten, oder in Ermangelung dessen, was würden Sie alle betrachten, um den besten Weg zu glätten eine flache Liste wie diese, Ausgleich Leistung und Lesbarkeit?

Ich habe versucht, eine solche Liste mit einem verschachtelten Listenverständnis zu glätten, etwa so:

[image for image in menuitem for menuitem in list_of_menuitems]

Aber ich gerate in Schwierigkeiten durch die NameError Vielfalt dort, weil die name 'menuitem' is not defined . Nachdem ich gegoogelt und mich auf Stack Overflow umgesehen hatte, erhielt ich die gewünschten Ergebnisse mit einem reduce Erklärung:

reduce(list.__add__, map(lambda x: list(x), list_of_menuitems))

Aber diese Methode ist ziemlich unleserlich, denn ich brauche die list(x) Aufruf dort, weil x ein Django ist QuerySet Objekt.

Schlussfolgerung :

Vielen Dank an alle, die zu dieser Frage beigetragen haben. Hier ist eine Zusammenfassung von dem, was ich gelernt habe. Ich mache dies auch zu einem Gemeinschafts-Wiki, falls andere diese Beobachtungen ergänzen oder korrigieren möchten.

Meine ursprüngliche Aussage zur Reduzierung ist redundant und sollte besser so formuliert werden:

>>> reduce(list.__add__, (list(mi) for mi in list_of_menuitems))

Dies ist die korrekte Syntax für ein verschachteltes Listenverständnis (Brillante Zusammenfassung dF !):

>>> [image for mi in list_of_menuitems for image in mi]

Aber keine dieser Methoden ist so effizient wie die Verwendung von itertools.chain :

>>> from itertools import chain
>>> list(chain(*list_of_menuitems))

Und wie @cdleary bemerkt, ist es wahrscheinlich besser, die Magie des Operators * zu vermeiden, indem man chain.from_iterable etwa so:

>>> chain = itertools.chain.from_iterable([[1,2],[3],[5,89],[],[6]])
>>> print(list(chain))
>>> [1, 2, 3, 5, 89, 6]

31 Stimmen

Ich verstehe nicht, warum alle map(lambda x: list(x), other) verwenden - ist das nicht gleichbedeutend mit map(list, other)? Das list builtin ist aufrufbar...

0 Stimmen

Es ist gleichwertig. Glücklicherweise hat Prairie Dogg erkannt, dass dieser Code hässlich ist :)

0 Stimmen

Ach ja, ich sehe, dass Sie in Ihrer Antwort darauf hingewiesen haben, @recursive. Entschuldigung für die Redundanz! :-)

4voto

DartLenin Punkte 999

Wenn Sie eine kompliziertere Liste mit nicht iterierbaren Elementen oder mit einer Tiefe von mehr als 2 haben, können Sie folgende Funktion verwenden:

def flat_list(list_to_flat):
    if not isinstance(list_to_flat, list):
        yield list_to_flat
    else:
        for item in list_to_flat:
            yield from flat_list(item)

Es gibt ein Generator-Objekt zurück, das Sie in eine Liste umwandeln können mit list() Funktion. Beachten Sie, dass yield from Syntax ist ab python3.3 verfügbar, aber Sie können stattdessen explizite Iteration verwenden.
Beispiel:

>>> a = [1, [2, 3], [1, [2, 3, [1, [2, 3]]]]]
>>> print(list(flat_list(a)))
[1, 2, 3, 1, 2, 3, 1, 2, 3]

0 Stimmen

Diese Lösung ergibt RECURSION ERROR ON : Eingabe A = ['str1', [[[['str2']]]], [['str3'], 'str4'], 'str5'] y A = [1.0, 2, 'a', [4,], [[6,], [8,]], [[[8,],[9,]], [[12,],[10]]]] . Wissen Sie, warum und wie man es beheben kann?

0 Stimmen

@anu Es funktionierte ohne Fehler auf Ihre Beispiele für mich (Python 3.7.1). Ich bin mir nicht sicher, warum es bei Ihnen nicht funktioniert.

0 Stimmen

Ich verwende Python 3.6, ich fand das Problem jetzt, Sie müssen hinzufügen or isinstance(list_to_flat, str) in die erste if-Bedingung einfügen, da sie vor Zeichenketten schützen muss. Ihre Lösung war perfekt für die Eingabe A = [1, [[[[2]]]], [[3], 4], 5] aber scheitert, wenn Sie Strings verwenden!, haben Test mit Strings in Python3.7?

4voto

devsaw Punkte 989

Diese Version ist ein Generator, den Sie anpassen können, wenn Sie eine Liste wünschen.

def list_or_tuple(l):
    return isinstance(l,(list,tuple))
## predicate will select the container  to be flattened
## write your own as required
## this one flattens every list/tuple

def flatten(seq,predicate=list_or_tuple):        
    ## recursive generator 
    for i in seq:
        if predicate(seq):
            for j in flatten(i):
                yield j
        else:
            yield i

Sie können ein Prädikat hinzufügen, wenn Sie diejenigen, die eine Bedingung erfüllen, reduzieren möchten

Entnommen aus dem Python-Kochbuch

4voto

Pierre Thibault Punkte 1695

Hier ist eine Version, die für mehrere Listenebenen funktioniert und collectons.Iterable :

import collections

def flatten(o, flatten_condition=lambda i: isinstance(i,
               collections.Iterable) and not isinstance(i, str)):
    result = []
    for i in o:
        if flatten_condition(i):
            result.extend(flatten(i, flatten_condition))
        else:
            result.append(i)
    return result

0 Stimmen

Könnten Sie mir bitte sagen, warum Ihre Lösung ein RecursionError: maximum recursion depth exceeded in comparison auf diese Eingabe A = ['image1', [[[['image2']]]], [['image3'], 'image4'], 'image5'] , während es gut läuft und diese Eingabe nicht abflacht A = [1,[2,3],[4,5,[6,[7,8],9]]]

1 Stimmen

Es handelt sich um ein Problem mit der Abflachungsbedingung. Da Zeichenketten iterierbar sind, werden sie als Zeichen reduziert, die ihrerseits Zeichenketten der Länge eins sind, und da sie Zeichenketten sind, wird dieselbe Logik erneut angewandt und es entsteht eine Endlosschleife. Also habe ich eine neue Version mit einer Flattening-Bedingung für mehr Kontrolle erstellt.

0 Stimmen

Großartig! Vielen Dank für die Aufklärung, jetzt funktioniert es! Ich habe Ihre Argumentation irgendwie verstanden, aber ich konnte sie nicht ganz verdauen. Könnten Sie mich bitte auf einen Artikel im Internet oder einen Beitrag verweisen, der mir hilft, das Problem zu verstehen! Was ich verstanden habe, ist ` ['image1'] -->['i','m','a','g','e','1'] `, d.h. Strings der Länge eins!, und jetzt, wie es in Endlosschleife gehen wird und was macht in Endlosschleife zu gehen? diesen Teil habe ich noch nicht verstanden! können Sie in irgendeiner Weise helfen!

3voto

kopos Punkte 408
def is_iterable(item):
   return isinstance(item, list) or isinstance(item, tuple)

def flatten(items):
    for i in items:
        if is_iterable(item):
            for m in flatten(i):
                yield m
        else:
            yield i

Test:

print list(flatten2([1.0, 2, 'a', (4,), ((6,), (8,)), (((8,),(9,)), ((12,),(10)))]))

1 Stimmen

Dies kann dazu führen, dass Zeichenketten in einzelne Zeichen zerlegt werden, was möglicherweise nicht beabsichtigt ist.

1 Stimmen

Ja, diese Bedingung habe ich nicht berücksichtigt. Danke.

0 Stimmen

@kopos, danke für Ihre Lösung, aber ich erhalte diesen Fehler for m in flatten(i): [Previous line repeated 996 more times] RecursionError: maximum recursion depth exceeded über Ihre Eingaben A = [1.0, 2, 'a', (4,), ((6,), (8,)), (((8,),(9,)), ((12,),(10)))] y A = ['str1', [[[['str2']]]], [['str3'], 'str4'], 'str5'] aber bei dieser Eingabe funktioniert es einwandfrei A = [1, [[[[2]]]], [[3], 4], 5] . Wissen Sie, was ist der Grund für das Versagen? und wie es zu beheben? irgendwelche Vorschläge?

3voto

Bottersnike Punkte 76

Wenn Sie nach einem integrierten, einfachen Einzeiler suchen, den Sie verwenden können:

a = [[1, 2, 3], [4, 5, 6]
b = [i[x] for i in a for x in range(len(i))]
print b

gibt zurück.

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X