Python >> Tutoriel Python >  >> Python

La manière la plus pythonique de supprimer plusieurs éléments d'une liste

La structure de données de liste intégrée de Python possède de nombreuses méthodes puissantes avec lesquelles tout programmeur Python avancé doit être familier. Cependant, certaines opérations sur les listes ne peuvent pas être effectuées simplement en appelant la bonne méthode.

Vous pouvez ajouter un seul élément à une liste en utilisant la méthode append(item) sur la liste. Si vous souhaitez ajouter une liste d'éléments à une autre liste, il existe la méthode expand(items) qui fait le travail pour vous.

Il en va de même si vous souhaitez supprimer un élément d'une liste, il vous suffit d'appeler la méthode remove(item) et vous obtenez le résultat souhaité.

Mais, vous êtes-vous déjà demandé comment supprimer une liste d'éléments d'une liste donnée ? Ou si les indices des éléments à supprimer étaient donnés, comment feriez-vous cela ?

Telles étaient les questions que je me posais dans l'un de mes derniers projets de loisirs. J'ai donc décidé de trouver la manière la plus Pythonique de le faire.

Problème

Encadrons notre problème comme ceci :étant donné une liste d'éléments de tâche, comment pouvons-nous supprimer tous les éléments de la liste qui sont marqués comme terminés ?

Actuellement, l'implémentation se présente comme suit :

class Task:
    def __init__(self, title):
        self.title = title
        self.done = False
        self.done_by = None
        
    def is_done(self):
        return self.done
    
    def set_done(self, name):
        self.done = True
        self.done_by = name
    
    def __repr__(self):
        state = f'was done by {self.done_by}' if self.done else 'is not done'
        s = f'Task: {self.title} {state}'
        return s
    
    
todo_list = [
    Task('Clean House'),
    Task('Walk Dog'),
    Task('Buy Bread'),
    Task('Repair Car'),
    Task('Plant Tree'),
    Task('Water Flowers'),
    Task('Bake Cake')
]


todo_list[0].set_done('Bob')
todo_list[2].set_done('Alice')
todo_list[5].set_done('Bob')

# print the whole list
print(todo_list)

Alors, comment pouvons-nous nettoyer notre liste de tâches afin qu'elle ne contienne que des tâches qui n'ont pas encore été effectuées ?

Solutions

Les solutions suivantes peuvent être divisées en deux groupes :

  1. Supprimer des éléments d'indices donnés
  2. Supprimer des éléments selon une certaine condition

Toute solution du premier type peut également être utilisée pour supprimer les éléments par une condition donnée. Pour ce faire, tout ce que nous avons à faire est de parcourir une fois la liste d'entrée, de vérifier la condition et de stocker les indices des éléments pour lesquels la condition était True . Cela peut être implémenté comme suit :

indices = []
for idx, task in enumerate(todo_list):
    if task.is_done():
        indices.append(idx)

Puisqu'il faut une itération de la liste pour trouver les indices, cela ajoute O(n) à la complexité d'exécution. Pourtant, puisque toute solution a au moins une complexité temporelle de O(n) , nous pouvons négliger cette première étape.

Méthode 1 :Supprimer un seul élément de la liste et répéter en boucle

Comme mentionné précédemment, il existe des méthodes pour supprimer un seul élément d'une liste, soit par valeur, soit par index.

Par conséquent, une solution pour supprimer plusieurs éléments consiste à utiliser une méthode qui supprime un seul élément et l'exécute en boucle. Cependant, il y a un écueil à cette solution. Après avoir supprimé l'élément à l'index 0, tous les autres éléments se déplacent et leurs indices changent car l'élément à l'index 1 est maintenant à l'index 0 et ainsi de suite.

Voici à quoi ressemblerait la solution en tant que code :

1.1. Supprimer en utilisant pop()

Le list.pop() la méthode supprime et renvoie le dernier élément d'un list existant . Le list.pop(index) méthode avec l'argument facultatif index supprime et retourne l'élément à la position index .

indices = [0, 2, 5] # must be ordered!
shift = 0
for i in indices:
    todo_list.pop(i-shift)
    shift += 1

Eh bien, cela vous semble probablement un peu gênant, et rassurez-vous, ce n'est pas comme vous le feriez en Python !

Pour éviter le décalage, nous pouvons inverser le tri de la liste des indices afin de pouvoir supprimer les éléments de la fin au début :

indices = [0, 2, 5]
for i in sorted(indices, reverse=True):
    todo_list.pop(i)

1.2. Supprimer en utilisant remove()

Une solution un peu plus simple, mais toujours pas la meilleure solution, utilise la méthode re move(item) .

Nous parcourons la liste et vérifions pour chaque élément s'il satisfait à la condition afin qu'il puisse être supprimé. Cette solution ressemblerait à ceci :

for task in todo_list:
    if task.is_done():
        todo_list.remove(task)

Attention si vous utilisez remove(item) sur une liste de types de données simples comme des entiers. La fonction remove() supprime la première occurrence de la valeur donnée de la liste !

Dans toutes les solutions ci-dessus, nous avons effectué la suppression sur place, ce qui signifie que nous avons conservé l'instance initiale de la liste.

Vous devriez maintenant voir qu'une bonne solution au problème n'est pas si évidente.

1.3. Supprimer en utilisant itemgetter() et remove()

Si vous utilisez la fonction itemgetter du module operator il existe une autre solution intéressante qui est essentiellement une amélioration de la solution 1.1.

La fonction itemgetter prend un nombre arbitraire d'indices et renvoie tous les éléments de ces indices dans un tuple. Voici l'implémentation de la solution proposée :

from operator import itemgetter

indices = [0, 2, 5]
for item in (itemgetter(*idx)(todo_list)):
    xs.remove(item)

Néanmoins, le code est plus complexe qu'il ne devrait l'être.

Méthode 2. Supprimer plusieurs éléments d'une liste

Dans les solutions précédentes, nous avons simplement adapté la fonctionnalité de suppression d'un seul élément afin de pouvoir l'utiliser dans une boucle. Dans cette section, nous examinons d'autres solutions Pythonic pour le problème.

2.1. Supprimer tous les éléments d'une liste

Si vous souhaitez supprimer tous les éléments de la liste, il existe une solution très simple :utilisez la méthode de la classe list clear() . Il supprime tous les éléments de la liste sur place.

2.2. Supprimer une tranche d'une liste

Si vos éléments sont dans une plage continue ou s'ils ont des distances au moins égales les uns des autres, un moyen simple de supprimer plusieurs éléments d'une liste consiste à utiliser le mot-clé del avec le tranchage.

Cela pourrait ressembler à ceci :

del todo_list[1::2]

Il supprime les éléments sur place, cependant, cela n'aide pas si nous voulons supprimer des éléments distribués au hasard de notre liste.

2.3. Supprimer des éléments distribués de manière aléatoire d'une liste à l'aide d'opérations d'ensemble

Tout d'abord, nous parcourons la liste une fois et extrayons tous les éléments à supprimer. Ensuite, nous convertissons les deux listes en ensembles et effectuons la suppression à l'aide d'opérations d'ensemble. Cela ressemble à ceci :

done = []
for task in todo_list:
    if task.is_done():
        done.append(task)
        
todo_list = list(set(todo_list) - set(done))

Sous le capot, un ensemble en Python est un hashmap qui permet d'effectuer certaines opérations sur des ensembles très rapidement (O(1) ). Malheureusement, nous devons convertir une liste en un ensemble et inversement, de sorte que nous perdons l'avantage en termes de vitesse. Et encore une fois, on se retrouve avec un O(n) solution.

Pour plus d'informations sur la complexité de calcul des opérations Python, consultez notre article détaillé sur le sujet.

Cette solution ne fonctionne pas sur place et est un peu difficile à lire en raison des nombreuses conversions entre les structures de données.

2.4. Supprimer des éléments distribués aléatoirement d'une liste à l'aide de la compréhension de liste

La meilleure façon de faire cela en Python est en fait très proche de ce que nous avons vu dans la première section de cet article où nous avons parcouru la liste et supprimé les éléments pour lesquels une certaine condition était vraie.

Cependant, dans cette solution, nous procéderons dans l'autre sens :nous itérons sur l'ancienne liste et créons une nouvelle liste à laquelle nous ajoutons tous les éléments que nous souhaitons conserver. Évidemment, nous devons créer une nouvelle liste pour y parvenir, de sorte que la solution ne fonctionnera pas sur place.

Python fournit exactement ce dont nous avons besoin pour obtenir le résultat souhaité en une seule ligne de code :les compréhensions de liste.

todo_list = [task for task in todo_list if not task.is_done()]

Si nous attribuons le résultat de la compréhension de la liste à notre todo_list initial variable, cette variable pointera désormais vers une liste contenant uniquement les tâches qui n'ont pas encore été effectuées.

Après la ligne de code ci-dessus, l'adresse mémoire à laquelle la variable todo_list points a changé !

Cependant, c'est ainsi que vous devez supprimer plusieurs éléments d'une liste en Python. Si vous souhaitez le faire sur place, il existe également une solution en une ligne au problème, cependant, personnellement, je ne vous recommanderais pas de l'utiliser.

Voici le code :

[todo_list.remove(task) for task in todo_list if task.is_done()]

Soyez honnête, combien de temps vous a-t-il fallu pour comprendre cela ?

Nous utilisons une compréhension de liste factice dans laquelle nous supprimons les éléments sélectionnés de la liste initiale, enfin nous jetons la liste résultante de la compréhension de liste.

Donc, ce que nous faisons en réalité, c'est d'abuser de la compréhension de la liste pour parcourir todo_list et supprimez-en des éléments.

Conclusion

Selon la répartition des éléments dans la liste, il existe différentes solutions.

  1. Si vous souhaitez supprimer tous les éléments d'une liste, utilisez la méthode de la liste clear() .
  2. Si vous souhaitez supprimer une plage continue de la liste ou si vous souhaitez supprimer des éléments séparés par des distances égales, utilisez le découpage avec l'opérateur del l[start:stop] .
  3. Si vous souhaitez supprimer des éléments distribués de manière aléatoire, utilisez une compréhension de liste qui sélectionne uniquement les éléments que vous souhaitez conserver :c'est la solution que je recommande.

Évidemment, il y a plus de possibilités pour résoudre le problème, pourtant, les solutions présentées dans cet article sont les plus courantes et aussi les plus faciles à comprendre. Si vous trouvez une autre solution intéressante, n'hésitez pas à nous contacter ! Nous aimerions le voir.

Où aller à partir d'ici ?

Assez de théorie, passons à la pratique !

Pour réussir dans le codage, vous devez vous lancer et résoudre de vrais problèmes pour de vraies personnes. C'est ainsi que vous pouvez facilement devenir un revenu à six chiffres. Et c'est ainsi que vous peaufinez les compétences dont vous avez vraiment besoin dans la pratique. Après tout, à quoi sert la théorie de l'apprentissage dont personne n'a jamais besoin ?

Les projets de pratique sont la façon dont vous aiguisez votre scie dans le codage !

Voulez-vous devenir un maître du code en vous concentrant sur des projets de code pratiques qui vous rapportent réellement de l'argent et résolvent des problèmes pour les gens ?

Alors devenez développeur Python freelance ! C'est la meilleure façon d'aborder la tâche d'améliorer vos compétences en Python, même si vous êtes un débutant complet.

Rejoignez mon webinaire gratuit "Comment développer votre Python de compétences pour les hauts revenus" et regardez comment j'ai développé mon entreprise de codage en ligne et comment vous pouvez aussi, dans le confort de votre foyer.

Rejoignez le webinaire gratuit maintenant !