2013-03-22 12 views
5

Ho impostato di oggetti:Come rimuovere i duplicati nel set per gli oggetti?

class Test(object): 
    def __init__(self): 
     self.i = random.randint(1,10) 


res = set() 

for i in range(0,1000): 
    res.add(Test()) 

print len(res) = 1000 

Come rimuovere i duplicati dal set di oggetti?

Grazie per le risposte, è un lavoro:

class Test(object): 
    def __init__(self, i): 
     self.i = i 
    # self.i = random.randint(1,10) 
    # self.j = random.randint(1,20) 

    def __keys(self): 
     t =() 
     for key in self.__dict__: 
      t = t + (self.__dict__[key],) 
     return t 

    def __eq__(self, other): 
     return isinstance(other, Test) and self.__keys() == other.__keys() 

    def __hash__(self): 
     return hash(self.__keys()) 

res = set() 

res.add(Test(2)) 
... 
res.add(Test(8)) 

risultato: [2,8,3,4,5,6,7]

ma come risparmiare ordine? Imposta non supporta l'ordine. Posso usare la lista invece impostare per esempio?

risposta

9

Gli oggetti devono essere hashable (cioè deve avere __eq__() e __hash__() definito) per i set per funzionare correttamente con loro:

class Test(object): 
    def __init__(self): 
     self.i = random.randint(1, 10) 

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

    def __hash__(self): 
     return self.i 

Un oggetto è hashable se ha un valore hash che non cambia mai durante la sua vita (necessita di un metodo __hash__()) e può essere paragonato ad altri oggetti (necessita di un metodo __eq__() o __cmp__()). Gli oggetti che possono essere confrontati devono avere lo stesso valore di hash.

La facilità di utilizzo rende un oggetto utilizzabile come chiave del dizionario e membro dell'insieme, poiché queste strutture dati utilizzano internamente il valore dell'hash.

 

Se si dispone di diversi attributi, hash e confrontare una tupla di loro (grazie, delnan):

class Test(object): 
    def __init__(self): 
     self.i = random.randint(1, 10) 
     self.k = random.randint(1, 10) 
     self.j = random.randint(1, 10) 

    def __eq__(self, other): 
     return (self.i, self.k, self.j) == (other.i, other.k, other.j) 

    def __hash__(self): 
     return hash((self.i, self.k, self.j)) 
+0

grazie, ma se ho qualche attrs? – Bdfy

+0

Combinali per produrre un hash intero univoco (usa la funzione buitlin 'hash' su attributi non interi e'^'loro insieme, per esempio) e definisci l'uguaglianza in un modo che abbia senso per te. Cosa devono avere in comune questi due oggetti per considerarli duplicati? Esprimilo in '__eq__'. –

+2

Il modo più semplice per definire l'uguaglianza e l'hash è trovare una tupla isomorfa all'oggetto e quindi delegare a tuple '__hash__' e' __eq__', usando 'collections.namedtuple' (se applicabile) o costruendo le tuple su richiesta: 'def __hash __ (self): return hash ((self.x, self.y, self.z))'. – delnan

0

Penso che si può facilmente fare quello che vuoi con una lista come hai chiesto nel tuo primo post da quando hai definito l'operatore eq:

l = [] 
if Test(0) not in l : 
    l.append(Test(0)) 

My 2 cts ...

0

La risposta di Pavel Anossov è ottima per consentire alla classe di essere utilizzata in un set con la semantica desiderata. Tuttavia, se vuoi preservare l'ordine dei tuoi articoli, avrai bisogno di un po 'di più. Ecco una funzione che de-duplica una lista, fino a quando gli elementi della lista sono hashable:

def dedupe(lst): 
    seen = set() 
    results = [] 
    for item in lst: 
     if item not in seen: 
      seen.add(item) 
      results.append(item) 
    return results 

Una versione leggermente più idiomatica sarebbe un generatore, piuttosto che una funzione che restituisce una lista. Questo elimina la variabile results, usando yield piuttosto che aggiungendo i valori univoci ad esso. Ho anche rinominato il parametro lst in iterable, poiché funzionerà altrettanto bene su qualsiasi oggetto iterabile (come un altro generatore).

+0

Non devi scrivere tu stesso; è già nelle [ricette itertools] (http://docs.python.org/2/library/itertools.html#recipes) come 'unique_everseen'. Oltre ad essere già scritto, ben testato e ottimizzato, accetta anche una funzione 'chiave'. Quindi, copialo sul tuo codice e usalo, o "pip installa more-itertools" e importalo da lì. – abarnert

1

La prima domanda è già stata risolta da Pavel Anossov.

Ma avete un'altra domanda:

ma come risparmiare ordine? Imposta non supporta l'ordine.Posso usare la lista invece impostare per esempio?

È possibile utilizzare un list, ma ci sono alcuni aspetti negativi:

  • si ottiene l'interfaccia sbagliata.
  • Non si ottiene la gestione automatica dei duplicati. Devi scrivere esplicitamente if foo not in res: res.append(foo). Ovviamente, puoi racchiuderlo in una funzione invece di scriverlo ripetutamente, ma è ancora un lavoro extra.
  • Sarà molto meno efficiente se la raccolta può diventare grande. In sostanza, l'aggiunta di un nuovo elemento, il controllo dell'esistenza di un elemento, ecc. Saranno tutti O (N) anziché O (1).

Quello che vuoi è qualcosa che funziona come un set ordinato. O, in modo equivalente, come un list che non consente duplicati.

Se fate tutto il vostro aggiunge prima, e poi tutte le ricerche, e non hai bisogno di ricerche di essere veloce, è possibile aggirare il problema in primo luogo la costruzione di un list, quindi utilizzando unique_everseen dal itertools recipes per rimuovere i duplicati.

Oppure si può semplicemente tenere un set e list o gli elementi per ordine (o un list più una set di elementi visti finora). Ma può essere un po 'complicato, quindi potresti voler concludere.

Idealmente, si desidera racchiuderlo in un tipo che ha esattamente la stessa API di set. Qualcosa come un OrderedSet simile a collections.OrderedDict.

Fortunatamente, se si scorre verso la parte inferiore della pagina di documenti, vedrete che esattamente quello che volete già esiste; c'è un collegamento a una ricetta OrderedSet in ActiveState.

Quindi, copialo, incollalo nel tuo codice, quindi cambia semplicemente res = set() in res = OrderedSet() e il gioco è fatto.

Problemi correlati