Disons que j'ai deux listes, l1
et l2
. Je souhaite effectuer l1 - l2
, qui renvoie tous les éléments de l1
et non dans l2
.
Je peux penser à une approche de boucle naïve pour le faire, mais cela va être vraiment inefficace. Qu'est-ce qu'un moyen pythonique efficace de faire cela?
Par exemple, si j'ai l1 = [1,2,6,8] and l2 = [2,3,5,8]
, l1 - l2
devrait renvoyer [1,6]
Python a une fonctionnalité de langage appelée List Comprehensions qui est parfaitement adaptée pour rendre ce genre de choses extrêmement facile. L'instruction suivante fait exactement ce que vous voulez et stocke le résultat dans l3
:
l3 = [x for x in l1 if x not in l2]
l3
contiendra [1, 6]
.
Une façon consiste à utiliser des ensembles:
>>> set([1,2,6,8]) - set([2,3,5,8])
set([1, 6])
En développant la réponse de Donut et les autres réponses ici, vous pouvez obtenir des résultats encore meilleurs en utilisant une compréhension de générateur au lieu d'une compréhension de liste, et en utilisant une structure de données set
(puisque l'opérateur in
est O(n) sur une liste mais O(1) sur un ensemble).
Alors, voici une fonction qui fonctionnerait pour vous:
def filter_list(full_list, excludes):
s = set(excludes)
return (x for x in full_list if x not in s)
Le résultat sera un résultat itératif qui va chercher paresseusement la liste filtrée. Si vous avez besoin d’un objet de liste réel (par exemple, si vous devez créer une len()
sur le résultat), vous pouvez facilement créer une liste comme ceci:
filtered_list = list(filter_list(full_list, excludes))
Vous pouvez également utiliser filter
avec l'expression lambda pour obtenir le résultat souhaité. Par exemple:
>>> l1 = [1,2,6,8]
>>> l2 = set([2,3,5,8])
# v `filter` returns the a iterator object. Here I'm type-casting
# v it to `list` in order to display the resultant value
>>> list(filter(lambda x: x not in l2, l1))
[1, 6]
Comparaison des performances
Ici, je compare la performance de toutes les réponses mentionnées ici. Comme prévu, l'opération --- d'Arkkset
est la plus rapide.
Différence de set d'Arkk - Premier [0.124 usec par boucle)
mquadri$ python -m timeit -s "l1 = set([1,2,6,8]); l2 = set([2,3,5,8]);" "l1 - l2"
10000000 loops, best of 3: 0.124 usec per loop
Compréhension de la liste de Daniel Pryden avec la recherche set
- Deuxième [0,302 usec par boucle)
mquadri$ python -m timeit -s "l1 = [1,2,6,8]; l2 = set([2,3,5,8]);" "[x for x in l1 if x not in l2]"
1000000 loops, best of 3: 0.302 usec per loop
Compréhension de la liste de donuts sur la liste - Troisième [0.552 usec par boucle)
mquadri$ python -m timeit -s "l1 = [1,2,6,8]; l2 = [2,3,5,8];" "[x for x in l1 if x not in l2]"
1000000 loops, best of 3: 0.552 usec per loop
Moinuddin Quadri utilise filter
- Quatrième [0.972 usec par boucle)
mquadri$ python -m timeit -s "l1 = [1,2,6,8]; l2 = set([2,3,5,8]);" "filter(lambda x: x not in l2, l1)"
1000000 loops, best of 3: 0.972 usec per loop
Akshay Hazari utilisant la combinaison de reduce
+ filter
- Cinquième [3.97 usec par boucle)
mquadri$ python -m timeit "l1 = [1,2,6,8]; l2 = [2,3,5,8];" "reduce(lambda x,y : filter(lambda z: z!=y,x) ,l1,l2)"
100000 loops, best of 3: 3.97 usec per loop
PS: set
ne conserve pas l'ordre et supprime les éléments en double de la liste. Par conséquent, n'utilisez pas set difference si vous en avez besoin.
Utilisez le type de jeu Python. Ce serait le plus pythonique. :)
De plus, étant donné qu’elle est native, elle devrait également être la méthode la plus optimisée.
Voir:
http://docs.python.org/library/stdtypes.html#set
http://docs.python.org/library/sets.htm (pour les pythons plus anciens)
# Using Python 2.7 set literal format.
# Otherwise, use: l1 = set([1,2,6,8])
#
l1 = {1,2,6,8}
l2 = {2,3,5,8}
l3 = l1 - l2
Solution alternative:
reduce(lambda x,y : filter(lambda z: z!=y,x) ,[2,3,5,8],[1,2,6,8])