Wie filtere ich effizient berechnete Werte innerhalb eines Python-Liste Verständnis?

StackOverflow https://stackoverflow.com/questions/130262

  •  02-07-2019
  •  | 
  •  

Frage

Die Python-Liste Verständnis Syntax macht es einfach, in einem Verständnis Wert zu filtern. Zum Beispiel:

result = [x**2 for x in mylist if type(x) is int]

Wird eine Liste der Quadrate der ganzen Zahlen in mylist zurückzukehren. Was aber, wenn der Test beinhaltet einige (teure) Berechnung und Sie auf das Ergebnis gefiltert werden soll? Eine Option ist:

result = [expensive(x) for x in mylist if expensive(x)]

Dies in einer Liste von Nicht- „false“ teuer (x) Werten führen, aber teuer () zweimal für jeden x. Gibt es ein Verständnis Syntax, die Sie diesen Test nur erlaubt, zu tun, während teuer telefonieren einmal pro x?

War es hilfreich?

Lösung

Wenn die Berechnungen sind bereits gut in Funktionen gebündelt, wie über die Verwendung von filter und map?

result = filter (None, map (expensive, mylist))

Sie können itertools.imap verwenden, wenn die Liste sehr groß ist.

Andere Tipps

Kam mit meiner eigenen Antwort nach einer Minute des Denkens auf. Es kann mit verschachtelten Comprehensions getan werden:

result = [y for y in (expensive(x) for x in mylist) if y]

Ich denke, das funktioniert, obwohl ich verschachtelte Comprehensions nur marginal lesbar finden sind

Der offensichtlichste (und ich würde behaupten, die meisten lesbar) Antwort ist nicht eine Liste Verständnis oder Generator Ausdruck zu verwenden, sondern eine echte Generator:

def gen_expensive(mylist):
    for item in mylist:
        result = expensive(item)
        if result:
            yield result

Es braucht mehr horizontalen Raum, aber es ist viel einfacher zu sehen, was es auf einen Blick der Fall ist, und Sie am Ende selbst nicht zu wiederholen.

result = [x for x in map(expensive,mylist) if x]

map () wird eine Liste der Werte der einzelnen Objekte in mylist Rückkehr zu teuer () übergeben. Dann können Sie die Liste-begreifen, dass, und unnötige Werte verwerfen.

Dieser

ist so etwas wie ein verschachteltes Verständnis, sollte aber schneller sein (da das Python-Interpreter kann es optimiert ziemlich leicht).

Das ist genau das, was Generatoren geeignet sind, zu behandeln:

result = (expensive(x) for x in mylist)
result = (do_something(x) for x in result if some_condition(x))
...
result = [x for x in result if x]  # finally, a list
  1. Das macht es völlig klar, was während jeder Stufe der Pipeline passiert.
  2. Explicit über implizit
  3. Verwendet Generatoren überall bis zum letzten Schritt, so dass keine großen Zwischenlisten

cf: 'Generator Tricks für System-Programmierer' von David Beazley

Sie können immer memoize die expensive() Funktion, so dass es beim zweiten Mal Aufruf ist lediglich eine Suche nach dem berechneten Wert von x.

Hier ist nur eine von vielen Implementierungen von memoize als Dekorateur .

Sie könnten teuer memoize (x) (und wenn Sie teuer (x) fordern häufig, sollten Sie wahrscheinlich es memoize jede mögliche Weise Diese Seite gibt eine Implementierung von memoize für Python.

http://code.activestate.com/recipes/52201/

Dies hat den zusätzlichen Vorteil, dass teuer (x) ausgeführt werden kann weniger als N-mal, da alle doppelten Einträge Verwendung des Memos aus der vorherige Ausführung machen.

Beachten Sie, dass diese teuer (x) übernimmt eine wahre Funktion und hängt nicht von äußeren Zustand, der geändert werden kann. Wenn teuer (x) nicht auf externen Zustand abhängt, und Sie können, wenn die betreffenden Zustandsänderungen erkennen, oder Sie wissen, dass es werden nicht Wechsel während Ihrer Liste Verständnisses, dann können Sie die Memos vor dem Verständnis zurückgesetzt.

Ich habe eine Vorliebe für:

itertools.ifilter(bool, (expensive(x) for x in mylist))

Dies hat den Vorteil auf:

Es ist die gute alte Verwendung eines for Schleife zu einer Liste anzuhängen, auch:

result = []
for x in mylist:
    expense = expensive(x)
    if expense:
        result.append(expense)
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top