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! :-)

25voto

jfs Punkte 370717

In Python 2.6 ist die Verwendung von chain.from_iterable() :

>>> from itertools import chain
>>> list(chain.from_iterable(mi.image_set.all() for mi in h.get_image_menu()))

Dadurch wird die Erstellung einer Zwischenliste vermieden.

24voto

S.Lott Punkte 371691

Leistungsergebnisse. Überarbeitet.

import itertools
def itertools_flatten( aList ):
    return list( itertools.chain(*aList) )

from operator import add
def reduce_flatten1( aList ):
    return reduce(add, map(lambda x: list(x), [mi for mi in aList]))

def reduce_flatten2( aList ):
    return reduce(list.__add__, map(list, aList))

def comprehension_flatten( aList ):
    return list(y for x in aList for y in x)

Ich habe eine 2-stufige Liste mit 30 Einträgen 1000 Mal geglättet

itertools_flatten     0.00554
comprehension_flatten 0.00815
reduce_flatten2       0.01103
reduce_flatten1       0.01404

Reduzieren ist immer eine schlechte Wahl.

5 Stimmen

map(lambda x: list(x), [mi for mi in aList]))map(list, aList) .

0 Stimmen

reduce_flatten = lambda list_of_iters: reduce(list.__add__, map(list, list_of_iters))

0 Stimmen

itertools_flatten2 = lambda aList: list(itertools.chain.from_iterable(aList))

16voto

Meitham Punkte 8241

Es scheint eine Verwechslung vorzuliegen mit operator.add ! Wenn Sie zwei Listen addieren, lautet der korrekte Begriff dafür concat , nicht hinzufügen. operator.concat ist das, was Sie verwenden müssen.

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

>>> 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, reduce respektiert den Sequenztyp, 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, Sie erhalten eine Liste zurück.

Wie sieht es mit der Leistung aus::

>>> 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 zum Reduzieren mit 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

0 Stimmen

Es ist wahrscheinlich die beste Lösung für eine Ebene der Verschachtelung. Aber das könnte eine zu restriktive Einschränkung sein. YMMV

9voto

Josh Lee Punkte 159535

Hier ist die korrekte Lösung unter Verwendung von List Comprehensions (sie sind in der Frage rückwärts gerichtet):

>>> join = lambda it: (y for x in it for y in x)
>>> list(join([[1,2],[3,4,5],[]]))
[1, 2, 3, 4, 5]

In Ihrem Fall wäre das

[image for menuitem in list_of_menuitems for image in menuitem.image_set.all()]

oder Sie könnten verwenden join und sagen

join(menuitem.image_set.all() for menuitem in list_of_menuitems)

In beiden Fällen war das Problem die Verschachtelung der for Schleifen.

8voto

recursive Punkte 80517

Meines Erachtens kann man das Lambda weglassen:

reduce(list.__add__, map(list, [mi.image_set.all() for mi in list_of_menuitems]))

Oder sogar die Karte abschaffen, denn Sie haben ja schon einen Listen-Comp:

reduce(list.__add__, [list(mi.image_set.all()) for mi in list_of_menuitems])

Sie können dies auch einfach als Summe von Listen ausdrücken:

sum([list(mi.image_set.all()) for mi in list_of_menuitems], [])

0 Stimmen

Sie könnten einfach addieren, und ich glaube, das zweite Argument für sum ist überflüssig.

2 Stimmen

Es ist nicht überflüssig. Der Standardwert ist Null, was zu TypeError: unsupported operand type(s) for + führt: 'int' und 'list'. IMO ist sum() direkter als reduce(add, ...)

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