Je me suis trouvé avoir un besoin de filtrage de base: j'ai une liste et je dois la filtrer par un attribut des éléments.
Mon code ressemblait à ceci:
my_list = [x for x in my_list if x.attribute == value]
Mais alors j'ai pensé, ne serait-il pas préférable de l'écrire comme ça?
my_list = filter(lambda x: x.attribute == value, my_list)
C'est plus lisible, et si nécessaire pour la performance, le lambda pourrait être retiré pour gagner quelque chose.
La question est: y a-t-il des réserves à utiliser la deuxième façon? Une différence de performance? Suis-je complètement absent du Pythonic Way ™ et dois-je le faire d'une autre manière (comme utiliser itemgetter au lieu du lambda)?
filter
c'était plus lisible. Lorsque vous avez une expression simple qui peut être utilisée telle quelle dans un listcomp, mais doit être enveloppée dans un lambda (ou construit de manière similaire à partir departial
ou desoperator
fonctions, etc.) pour passerfilter
, c'est là que les listcomps gagnent.filter
est un objet générateur de filtre et non une liste.Réponses:
Il est étrange combien la beauté varie selon les personnes. Je trouve la compréhension de la liste beaucoup plus claire que
filter
+lambda
, mais utilisez celle que vous trouvez plus facilement.Il y a deux choses qui peuvent ralentir votre utilisation
filter
.Le premier est la surcharge de l'appel de fonction: dès que vous utilisez une fonction Python (qu'elle soit créée par
def
oulambda
), il est probable que le filtre soit plus lent que la compréhension de la liste. Ce n'est certainement pas suffisant pour avoir de l'importance, et vous ne devriez pas penser beaucoup aux performances avant d'avoir chronométré votre code et trouvé qu'il s'agit d'un goulot d'étranglement, mais la différence sera là.L'autre surcharge qui pourrait s'appliquer est que le lambda est forcé d'accéder à une variable de portée (
value
). C'est plus lent que d'accéder à une variable locale et dans Python 2.x, la compréhension de la liste accède uniquement aux variables locales. Si vous utilisez Python 3.x, la compréhension de la liste s'exécute dans une fonction distincte, elle sera donc également accessiblevalue
via une fermeture et cette différence ne s'appliquera pas.L'autre option à considérer est d'utiliser un générateur au lieu d'une compréhension de liste:
Ensuite, dans votre code principal (où la lisibilité importe vraiment), vous avez remplacé la compréhension de la liste et le filtre par un nom de fonction, espérons-le, significatif.
la source
[]
à()
. De plus, je suis d'accord que la liste de composition est plus belle.filter
être plus rapide en utilisant une fonction de rappel Python.Il s'agit d'un problème quelque peu religieux en Python. Même si Guido a envisagé de supprimer
map
,filter
etreduce
de Python 3 , il y avait suffisamment de jeu qui, à la fin, n'areduce
été déplacé des fonctions intégrées que vers functools.reduce .Personnellement, je trouve les listes de compréhension plus faciles à lire. Il est plus explicite ce qui se passe à partir de l'expression
[i for i in list if i.attribute == value]
car tout le comportement est à la surface et non à l'intérieur de la fonction de filtre.Je ne m'inquiéterais pas trop de la différence de performance entre les deux approches car elle est marginale. Je n'optimiserais vraiment cela que s'il s'avérait être le goulot d'étranglement dans votre application, ce qui est peu probable.
De plus, puisque le BDFL voulait sortir
filter
du langage, cela rendait sûrement automatiquement les compréhensions de liste plus Pythonic ;-)la source
Étant donné que toute différence de vitesse est nécessairement minuscule, l'utilisation de filtres ou de listes de listes se résume à une question de goût. En général, je suis enclin à utiliser des compréhensions (ce qui semble être d'accord avec la plupart des autres réponses ici), mais il y a un cas où je préfère
filter
.Un cas d'utilisation très fréquent consiste à extraire les valeurs de certains X itérables soumis à un prédicat P (x):
mais parfois vous voulez d'abord appliquer une fonction aux valeurs:
Comme exemple spécifique, considérez
Je pense que cela semble légèrement meilleur que l'utilisation
filter
. Mais considérez maintenantDans ce cas, nous voulons
filter
comparer la valeur post-calculée. Outre le problème du calcul du cube deux fois (imaginez un calcul plus coûteux), il y a le problème de l'écriture de l'expression deux fois, violant la esthétique SEC . Dans ce cas, je serais apte à utiliserla source
[prime(i) for i in [x**3 for x in range(1000)]]
x*x*x
ne peut pas être un nombre premier, comme il l'a faitx^2
etx
comme facteur, l'exemple n'a pas vraiment de sens sur le plan mathématique, mais peut-être qu'il est toujours utile. (Peut-être pourrions-nous trouver quelque chose de mieux?)prime_cubes = filter(prime, (x*x*x for x in range(1000)))
prime_cubes = [1]
économiser à la fois les cycles de mémoire et de processeur ;-)[]
Bien que cela
filter
puisse être la "voie la plus rapide", la "voie Pythonic" ne serait pas de se soucier de telles choses à moins que les performances ne soient absolument critiques (auquel cas vous n'utiliseriez pas Python!).la source
Je pensais simplement ajouter qu'en python 3, filter () est en fait un objet itérateur, vous devez donc passer votre appel de méthode de filtrage à list () afin de construire la liste filtrée. Donc en python 2:
les listes b et c ont les mêmes valeurs et ont été complétées à peu près en même temps que filter () était équivalent [x pour x dans y si z]. Cependant, en 3, ce même code laisserait la liste c contenant un objet filtre, pas une liste filtrée. Pour produire les mêmes valeurs en 3:
Le problème est que list () prend un itérable comme argument et crée une nouvelle liste à partir de cet argument. Le résultat est que l'utilisation de filtre de cette manière en python 3 prend jusqu'à deux fois plus de temps que la méthode [x pour x en y si z] car vous devez répéter la sortie de filter () ainsi que la liste d'origine.
la source
Une différence importante est que la compréhension de la liste renverra un
list
tandis que le filtre renvoie unfilter
, que vous ne pouvez pas manipuler comme unlist
(c'est-à-dire: appelerlen
dessus, qui ne fonctionne pas avec le retour defilter
).Mon auto-apprentissage m'a amené à un problème similaire.
Cela étant dit, s'il existe un moyen d'avoir le résultat
list
d'unfilter
, un peu comme vous le feriez dans .NET lorsque vous le faiteslst.Where(i => i.something()).ToList()
, je suis curieux de le savoir.EDIT: C'est le cas pour Python 3, pas 2 (voir la discussion dans les commentaires).
la source
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()
sur le résultat:list(filter(my_func, my_iterable))
. Et bien sûr, vous pouvez remplacerlist
parset
, outuple
, ou toute autre chose qui prend un itérable. Mais pour quiconque autre que les programmeurs fonctionnels, le cas est encore plus fort d'utiliser une compréhension de liste plutôt qu'unefilter
conversion explicite verslist
.Je trouve la deuxième façon plus lisible. Il vous indique exactement quelle est l'intention: filtrer la liste.
PS: n'utilisez pas 'list' comme nom de variable
la source
filter
est généralement légèrement plus rapide si vous utilisez une fonction intégrée.Je m'attendrais à ce que la compréhension de la liste soit légèrement plus rapide dans votre cas
la source
Le filtre n'est que cela. Il filtre les éléments d'une liste. Vous pouvez voir que la définition mentionne la même chose (dans le lien de documentation officiel que j'ai mentionné précédemment). Alors que la compréhension de liste est quelque chose qui produit une nouvelle liste après avoir agi sur quelque chose de la liste précédente. , par exemple, un type de données entièrement nouveau. Comme la conversion d'entiers en chaîne, etc.)
Dans votre exemple, il est préférable d'utiliser le filtre que la compréhension de liste, selon la définition. Cependant, si vous le souhaitez, par exemple other_attribute à partir des éléments de la liste, dans votre exemple doit être récupéré en tant que nouvelle liste, vous pouvez utiliser la compréhension de la liste.
C'est ainsi que je me souviens de la compréhension des filtres et des listes. Supprimez quelques éléments d'une liste et conservez les autres éléments intacts, utilisez le filtre. Utilisez vous-même un peu de logique au niveau des éléments et créez une liste édulcorée adaptée à un objectif, utilisez la compréhension de la liste.
la source
Voici une courte pièce que j'utilise lorsque j'ai besoin de filtrer quelque chose après la compréhension de la liste. Juste une combinaison de filtre, lambda et listes (autrement connu comme la loyauté d'un chat et la propreté d'un chien).
Dans ce cas, je lis un fichier, je supprime les lignes vides, les lignes commentées et tout ce qui suit un commentaire sur une ligne:
la source
file_contents = list(filter(None, (s.partition('#')[0].strip() for s in lines)))
En plus de la réponse acceptée, il y a un cas d'angle où vous devez utiliser un filtre au lieu d'une compréhension de liste. Si la liste n'est pas partageable, vous ne pouvez pas la traiter directement avec une compréhension de liste. Un exemple réel est si vous utilisez
pyodbc
pour lire les résultats d'une base de données. LefetchAll()
résultatcursor
est une liste non partageable. Dans cette situation, pour manipuler directement les résultats retournés, un filtre doit être utilisé:Si vous utilisez la compréhension de liste ici, vous obtiendrez l'erreur:
la source
>>> hash(list()) # TypeError: unhashable type: 'list'
deuxièmement, cela fonctionne très bien:processed_data = [s for s in data_from_db if 'abc' in s.field1 or s.StartTime >= start_date_time]
Il m'a fallu un certain temps pour me familiariser avec le
higher order functions
filter
etmap
. Alors jefilter
me suis habitué à eux et j'ai vraiment aimé car il était explicite qu'il filtre en gardant ce qui est vrai et je me sentais cool que j'en connaissaisfunctional programming
termes.J'ai ensuite lu ce passage (Fluent Python Book):
Et maintenant, je pense, pourquoi s'embêter avec le concept de
filter
/map
si vous pouvez y parvenir avec des idiomes déjà largement répandus comme les listes de compréhension. De plusmaps
, cefilters
sont des fonctions. Dans ce cas, je préfère utiliser desAnonymous functions
lambdas.Enfin, juste pour le faire tester, j'ai chronométré les deux méthodes (
map
etlistComp
) et je n'ai vu aucune différence de vitesse pertinente qui justifierait de faire des arguments à ce sujet.la source
Curieusement sur Python 3, je vois que le filtre fonctionne plus rapidement que les compréhensions de liste.
J'ai toujours pensé que les listes de compréhension seraient plus performantes. Quelque chose comme: [nom pour le nom dans brand_names_db si le nom n'est pas None] Le bytecode généré est un peu meilleur.
Mais ils sont en réalité plus lents:
la source
if not None
dans la compréhension de la liste que vous êtes en train de définir une fonction lambda (remarquez laMAKE_FUNCTION
déclaration). Deuxièmement, les résultats sont différents, car la version de compréhension de liste supprimera uniquement laNone
valeur, tandis que la version de filtre supprimera toutes les valeurs "fausses". Cela dit, tout le but de la micro-analyse comparative est inutile. Ce sont un million d'itérations, multiplié par 1 000 articles! La différence est négligeable .Ma prise
la source
i
n'a jamais été considéré comme undict
, et il n'y en a pas besoinlimit
. En dehors de cela, en quoi est-ce différent de ce que le PO a suggéré et comment cela répond-il à la question?