2017-10-04 18 views
1

J'ai une liste d'instances d'une certaine classe. Cette liste contient des «doublons», en ce sens que les doublons partagent exactement les mêmes attributs. Je veux supprimer les doublons de cette liste.Comment supprimer `duplicates 'dans la liste des instances

Je peux vérifier si deux instances partagent les mêmes attributs à l'aide

class MyClass: 

    def __eq__(self, other) : 
     return self.__dict__ == other.__dict__ 

Je pourrais bien sûr parcourir la liste complète des instances et de les comparer élément par élément pour supprimer les doublons, mais je me demandais s'il est une façon plus pythonique de le faire, de préférence en utilisant la compréhension de l'opérateur in + list.

+7

Vous pouvez les rendre lavables et ensuite utiliser 'set' pour éliminer les doublons – vaultah

+1

NB: l'approche définie ne conservera aucun ordre dans votre liste. –

+0

Quelle version de Python? –

Répondre

4

set s (pas d'ordre)

Un ensemble ne peut pas contenir des éléments en double. list(set(content)) va dédoublonner une liste. Ce n'est pas trop inefficace et est probablement l'une des meilleures façons de le faire: P Vous devrez cependant définir une fonction __hash__ pour votre classe, qui doit être la même pour les éléments égaux et différente pour les éléments inégaux pour que cela fonctionne. Notez que la valeur hash doit obéir à la règle ci-dessus mais sinon, elle peut changer entre les exécutions sans causer de problèmes. Fonction

index (de l'ordre stable)

Vous pourriez faire lambda l: [l[index] for index in range(len(l)) if index == l.index(l[index])]. Cela ne conserve que les éléments qui sont les premiers dans la liste.

opérateur in (ordre stable)

def uniquify(content): 
    result = [] 
    for element in content: 
     if element not in result: 
      result.append(element) 
    return result 

Cela permet de garder insérer un élément dans la liste de sortie sauf si elles sont déjà dans la liste de sortie.

1

Un peu plus sur l'approche de l'ensemble. Vous pouvez implémenter un hachage en toute sécurité en déléguant à un hachage de tuple - juste hacher un tuple de tous les attributs que vous voulez regarder. Vous devrez également définir un __eq__ qui se comporte correctement.

class MyClass: 
    def __init__(self, a, b, c): 
     self.a = a 
     self.b = b 
     self.c = c 

    def __eq__(self, other): 
     return (self.a, self.b, self.c) == (other.a, other.b, other.c) 

    def __hash__(self): 
     return hash((self.a, self.b, self.c)) 

    def __repr__(self): 
     return "MyClass({!r}, {!r}, {!r})".format(self.a, self.b, self.c) 

Comme vous faites si bien la construction de tuple, vous pouvez simplement faire votre classe itérables:

def __iter__(self): 
    return iter((self.a, self.b, self.c)) 

Cela vous permet d'appeler tuple sur self au lieu de faire laborieusement .a, .b, .c etc.

Vous pouvez alors faire quelque chose comme ceci:

def unordered_elim(l): 
    return list(set(l)) 

Si vous souhaitez conserver la commande, vous pouvez utiliser un OrderedDict à la place:

from collections import OrderedDict 

def ordered_elim(l): 
    return list(OrderedDict.fromkeys(l).keys()) 

Cela devrait être plus rapide que d'utiliser in ou index, tout en préservant la commande.Vous pouvez tester quelque chose comme ceci:

data = [MyClass("this", "is a", "duplicate"), 
     MyClass("first", "unique", "datum"), 
     MyClass("this", "is a", "duplicate"), 
     MyClass("second", "unique", "datum")] 

print(unordered_elim(data)) 
print(ordered_elim(data)) 

Avec cette sortie:

[MyClass('first', 'unique', 'datum'), MyClass('second', 'unique', 'datum'), MyClass('this', 'is a', 'duplicate')] 
[MyClass('this', 'is a', 'duplicate'), MyClass('first', 'unique', 'datum'), MyClass('second', 'unique', 'datum')] 

NB Si l'un de vos attributs ne sont pas indexables, cela ne fonctionnera pas, et vous aurez besoin soit contournez-le (modifiez une liste en un uplet) ou utilisez une approche lente, comme in.

+0

@ZachGates Je ne suis pas sûr de comprendre - en ce qui me concerne, 'MyClass (1, 2, 3)! = MyClass (3, 2, 1)' (ceci est également impliqué par OP - ils auront un "dict" différent.). Comme pour 'hash (self) == hash (other)' - la raison pour laquelle nous avons besoin de '__eq__' est de désambiguïser quand il y a une collision de hachage. –