Ich hatte zufällig ein grundlegendes Filterbedürfnis: Ich habe eine Liste und muss sie nach einem Attribut der Elemente filtern.
Mein Code sah folgendermaßen aus:
my_list = [x for x in my_list if x.attribute == value]
Aber dann dachte ich, wäre es nicht besser, es so zu schreiben?
my_list = filter(lambda x: x.attribute == value, my_list)
Es ist besser lesbar, und wenn es für die Leistung benötigt wird, kann das Lambda herausgenommen werden, um etwas zu gewinnen.
Die Frage ist: Gibt es irgendwelche Einschränkungen bei der Verwendung des zweiten Weges? Leistungsunterschiede? Vermisse ich den Pythonic Way ™ vollständig und sollte es auf eine andere Weise tun (z. B. mit Itemgetter anstelle des Lambda)?
filter
besser lesbar ist. Wenn Sie einen einfachen Ausdruck haben, der unverändert in einem Listencomputer verwendet werden kann, aber in ein Lambda (oder ein ähnliches Konstrukt auspartial
oderoperator
Funktionen usw.) eingeschlossen werden muss, an das übergeben werdenfilter
soll, dann gewinnen Listcomps.filter
ein Filtergeneratorobjekt ist, keine Liste.Antworten:
Es ist seltsam, wie unterschiedlich die Schönheit für verschiedene Menschen ist. Ich finde das Listenverständnis viel klarer als
filter
+lambda
, benutze aber das, was du leichter findest.Es gibt zwei Dinge, die Ihre Verwendung verlangsamen können
filter
.Der erste ist der Overhead des Funktionsaufrufs: Sobald Sie eine Python-Funktion verwenden (unabhängig davon, ob sie von
def
oder erstellt wurdelambda
), ist der Filter wahrscheinlich langsamer als das Listenverständnis. Es ist mit ziemlicher Sicherheit nicht genug, um eine Rolle zu spielen, und Sie sollten nicht viel über die Leistung nachdenken, bis Sie Ihren Code zeitlich festgelegt und festgestellt haben, dass es sich um einen Engpass handelt, aber der Unterschied wird da sein.Der andere Aufwand, der möglicherweise anfällt, besteht darin, dass das Lambda gezwungen wird, auf eine Gültigkeitsbereichsvariable (
value
) zuzugreifen . Das ist langsamer als der Zugriff auf eine lokale Variable und in Python 2.x greift das Listenverständnis nur auf lokale Variablen zu. Wenn Sie Python 3.x verwenden, wird das Listenverständnis in einer separaten Funktion ausgeführt, sodass der Zugriff auchvalue
über einen Abschluss erfolgt und dieser Unterschied nicht gilt.Die andere zu berücksichtigende Option ist die Verwendung eines Generators anstelle eines Listenverständnisses:
Dann haben Sie in Ihrem Hauptcode (wo die Lesbarkeit wirklich wichtig ist) sowohl das Listenverständnis als auch den Filter durch einen hoffentlich aussagekräftigen Funktionsnamen ersetzt.
quelle
[]
zu()
. Ich stimme auch zu, dass die Liste comp schöner ist.filter
mithilfe einer Python-Rückruffunktion feststellen , dass Sie schneller sind.Dies ist ein etwas religiöses Thema in Python. Obwohl Guido überlegte, es zu entfernen
map
,filter
undreduce
aus Python 3 , gab es genug Spiel, das am Ende nurreduce
von eingebauten zu functools.reduce verschoben wurde .Persönlich finde ich Listenverständnisse leichter zu lesen. Es ist expliziter, was mit dem Ausdruck geschieht,
[i for i in list if i.attribute == value]
da sich das gesamte Verhalten auf der Oberfläche befindet und nicht innerhalb der Filterfunktion.Ich würde mir keine Sorgen über den Leistungsunterschied zwischen den beiden Ansätzen machen, da dieser marginal ist. Ich würde dies wirklich nur optimieren, wenn sich herausstellen würde, dass der Engpass in Ihrer Anwendung unwahrscheinlich ist.
Auch da die BDFL dann
filter
aus der Sprache verschwunden sein wollte , macht das Listenverständnis sicherlich automatisch pythonischer ;-)quelle
Da jeder Geschwindigkeitsunterschied zwangsläufig winzig ist, hängt es vom Geschmack ab, ob Filter oder Listenverständnisse verwendet werden. Im Allgemeinen neige ich dazu, Verständnis zu verwenden (was mit den meisten anderen Antworten hier übereinzustimmen scheint), aber es gibt einen Fall, in dem ich es vorziehen würde
filter
.Ein sehr häufiger Anwendungsfall ist das Herausziehen der Werte eines iterierbaren X, das einem Prädikat P (x) unterliegt:
Manchmal möchten Sie jedoch zuerst eine Funktion auf die Werte anwenden:
Betrachten Sie als konkretes Beispiel
Ich denke, das sieht etwas besser aus als zu verwenden
filter
. Aber jetzt überlegen SieIn diesem Fall wollen wir
filter
gegen den nachberechneten Wert. Neben dem Problem, den Würfel zweimal zu berechnen (stellen Sie sich eine teurere Berechnung vor), gibt es das Problem, den Ausdruck zweimal zu schreiben, was die DRY- Ästhetik verletzt . In diesem Fall würde ich gerne verwendenquelle
[prime(i) for i in [x**3 for x in range(1000)]]
x*x*x
kann keine Primzahl sein, wie es istx^2
undx
als Faktor macht das Beispiel auf mathematische Weise keinen Sinn, aber vielleicht ist es immer noch hilfreich. (Vielleicht könnten wir aber etwas Besseres finden?)prime_cubes = filter(prime, (x*x*x for x in range(1000)))
prime_cubes = [1]
um sowohl Speicher- als auch CPU-Zyklen zu sparen[]
Obwohl
filter
dies der "schnellere Weg" sein mag, wäre der "pythonische Weg", sich nicht um solche Dinge zu kümmern, es sei denn, die Leistung ist absolut kritisch (in diesem Fall würden Sie Python nicht verwenden!).quelle
Ich dachte, ich würde nur hinzufügen, dass filter () in Python 3 tatsächlich ein Iteratorobjekt ist, sodass Sie Ihren Filtermethodenaufruf an list () übergeben müssen, um die gefilterte Liste zu erstellen. Also in Python 2:
Die Listen b und c haben dieselben Werte und wurden ungefähr zur gleichen Zeit abgeschlossen, als filter () äquivalent war [x für x in y, wenn z]. In 3 würde derselbe Code jedoch die Liste c verlassen, die ein Filterobjekt enthält, keine gefilterte Liste. So erzeugen Sie die gleichen Werte in 3:
Das Problem ist, dass list () eine iterable als Argument verwendet und aus diesem Argument eine neue Liste erstellt. Das Ergebnis ist, dass die Verwendung von Filter auf diese Weise in Python 3 bis zu doppelt so lange dauert wie die Methode [x für x in y wenn z], da Sie sowohl die Ausgabe von filter () als auch die ursprüngliche Liste durchlaufen müssen.
quelle
Ein wichtiger Unterschied besteht darin, dass das Listenverständnis eine
list
Weile zurückgibtfilter
, während der Filter ein zurückgibt , das Sie nicht wie ein manipulieren könnenlist
(dh: Rufen Sielen
es auf, was mit der Rückgabe von nicht funktioniertfilter
).Mein eigenes Selbstlernen brachte mich zu einem ähnlichen Thema.
Abgesehen davon bin ich neugierig , wenn es eine Möglichkeit gibt, das Ergebnis
list
von a zu erhaltenfilter
, ähnlich wie Sie es in .NET tun würdenlst.Where(i => i.something()).ToList()
, wenn Sie dies tun .BEARBEITEN: Dies ist bei Python 3 der Fall, nicht bei 2 (siehe Diskussion in den Kommentaren).
quelle
a = [1, 2, 3, 4, 5, 6, 7, 8]
f = filter(lambda x: x % 2 == 0, a)
lc = [i for i in a if i % 2 == 0]
>>> type(f)
<class 'filter'>
>>> type(lc)
<class 'list'>
list()
das Ergebnis auf :list(filter(my_func, my_iterable))
. Und natürlich können Sie ersetzenlist
mitset
, odertuple
, oder irgendetwas anderes , das eine iterable nimmt. Für andere als funktionale Programmierer ist es jedoch noch wichtiger, ein Listenverständnis zu verwenden, als einefilter
explizite Konvertierung inlist
.Ich finde den zweiten Weg besser lesbar. Es sagt Ihnen genau, was die Absicht ist: Filtern Sie die Liste.
PS: Verwenden Sie 'Liste' nicht als Variablennamen
quelle
Im Allgemeinen
filter
ist es etwas schneller, wenn eine integrierte Funktion verwendet wird.Ich würde erwarten, dass das Listenverständnis in Ihrem Fall etwas schneller ist
quelle
Filter ist genau das. Es filtert die Elemente einer Liste heraus. Sie können sehen, dass die Definition dasselbe erwähnt (in dem offiziellen Dokument-Link, den ich zuvor erwähnt habe). Während das Listenverständnis etwas ist, das eine neue Liste erzeugt, nachdem es auf etwas in der vorherigen Liste reagiert hat. (Sowohl das Filter- als auch das Listenverständnis erzeugen eine neue Liste und führen keine Operation anstelle der älteren Liste aus. Eine neue Liste hier ist so etwas wie eine Liste mit B. ein völlig neuer Datentyp. Wie das Konvertieren von Ganzzahlen in Zeichenfolgen usw.)
In Ihrem Beispiel ist es besser, Filter als Listenverständnis gemäß der Definition zu verwenden. Wenn Sie jedoch möchten, dass other_attribute aus den Listenelementen in Ihrem Beispiel als neue Liste abgerufen werden soll, können Sie das Listenverständnis verwenden.
So erinnere ich mich tatsächlich an das Filter- und Listenverständnis. Entfernen Sie einige Dinge in einer Liste und lassen Sie die anderen Elemente intakt. Verwenden Sie den Filter. Verwenden Sie eine eigene Logik für die Elemente und erstellen Sie eine verwässerte Liste, die für einen bestimmten Zweck geeignet ist. Verwenden Sie das Listenverständnis.
quelle
Hier ist ein kurzes Stück, das ich verwende, wenn ich nach dem Listenverständnis nach etwas filtern muss . Nur eine Kombination aus Filter, Lambda und Listen (auch bekannt als die Loyalität einer Katze und die Sauberkeit eines Hundes).
In diesem Fall lese ich eine Datei, entferne Leerzeilen, kommentiere Zeilen und alles nach einem Kommentar in einer Zeile:
quelle
file_contents = list(filter(None, (s.partition('#')[0].strip() for s in lines)))
Zusätzlich zur akzeptierten Antwort gibt es einen Eckfall, in dem Sie Filter anstelle eines Listenverständnisses verwenden sollten. Wenn die Liste nicht zerlegbar ist, können Sie sie nicht direkt mit einem Listenverständnis verarbeiten. Ein Beispiel aus der Praxis ist
pyodbc
das Lesen von Ergebnissen aus einer Datenbank. DasfetchAll()
Ergebnis voncursor
ist eine nicht zerlegbare Liste. In dieser Situation sollte ein Filter verwendet werden, um die zurückgegebenen Ergebnisse direkt zu bearbeiten:Wenn Sie hier das Listenverständnis verwenden, wird folgende Fehlermeldung angezeigt:
quelle
>>> hash(list()) # TypeError: unhashable type: 'list'
Zweitens funktioniert dies einwandfrei :processed_data = [s for s in data_from_db if 'abc' in s.field1 or s.StartTime >= start_date_time]
Ich brauchte einige Zeit, um mich mit dem
higher order functions
filter
und vertraut zu machenmap
. Alsofilter
habe ich mich an sie gewöhnt und es hat mir wirklich gut gefallen, da es explizit war, dass es filtert, indem es das hält, was wahr ist, und ich fühlte mich cool, dass ich einigefunctional programming
Begriffe kannte .Dann las ich diese Passage (Fluent Python Book):
Und jetzt denke ich, warum sollte man sich mit dem Konzept beschäftigen
filter
/map
wenn man es mit bereits weit verbreiteten Redewendungen wie Listenverständnis erreichen kann. Darüber hinausmaps
undfilters
sind Art von Funktionen. In diesem Fall bevorzuge ichAnonymous functions
Lambdas.Schließlich habe ich, nur um es testen zu lassen, beide Methoden (
map
undlistComp
) zeitlich festgelegt und keinen relevanten Geschwindigkeitsunterschied festgestellt, der es rechtfertigen würde, Argumente dafür vorzulegen.quelle
Seltsamerweise sehe ich in Python 3, dass Filter schneller arbeiten als Listenverständnisse.
Ich dachte immer, dass das Listenverständnis performanter sein würde. So etwas wie: [Name für Name in brand_names_db, wenn name nicht None ist] Der generierte Bytecode ist etwas besser.
Aber sie sind tatsächlich langsamer:
quelle
if not None
in der Liste Begreifen Sie sind , die eine Lambda - Funktion ( man beachte dieMAKE_FUNCTION
Anweisung). Zweitens sind die Ergebnisse unterschiedlich, da die Listenverständnisversion nur denNone
Wert entfernt, während die Filterversion alle "falschen" Werte entfernt. Trotzdem ist der gesamte Zweck des Mikrobenchmarkings nutzlos. Das sind eine Million Iterationen, mal 1.000 Artikel! Der Unterschied ist vernachlässigbar .Meine Einstellung
quelle
i
wurde nie als a bezeichnetdict
, und es besteht keine Notwendigkeit fürlimit
. Wie unterscheidet sich das von dem, was das OP vorgeschlagen hat, und wie beantwortet es die Frage?