Python ha un dizionario ordinato . Che dire di un set ordinato?
collections.Counterè la borsa di Python.
Python ha un dizionario ordinato . Che dire di un set ordinato?
collections.Counterè la borsa di Python.
Risposte:
C'è una ricetta ordinata (possibile nuovo collegamento ) per questo, a cui si fa riferimento dalla documentazione di Python 2 . Funziona su Py2.6 o successivo e 3.0 o successivo senza alcuna modifica. L'interfaccia è quasi esattamente la stessa di un set normale, tranne per il fatto che l'inizializzazione dovrebbe essere fatta con un elenco.
OrderedSet([1, 2, 3])
Questo è un MutableSet, quindi la firma per .unionnon corrisponde a quella del set, ma poiché include __or__qualcosa di simile può essere facilmente aggiunta:
@staticmethod
def union(*sets):
union = OrderedSet()
union.union(*sets)
return union
def union(self, *sets):
for set in sets:
self |= set
update, union, intersection.
unionnella stessa classe. L'ultimo "vincerà" e il primo non esisterà in fase di esecuzione. Questo perché OrderedSet.union(senza parentesi) deve fare riferimento a un singolo oggetto.
Le chiavi di un dizionario sono uniche. Pertanto, se si ignorano i valori in un dizionario ordinato (ad es. Assegnandoli None), si ha essenzialmente un set ordinato.
A partire da Python 3.1 c'è collections.OrderedDict. Di seguito è riportata un'implementazione di esempio di un OrderedSet. (Si noti che solo pochi metodi devono essere definiti o ignorati: collections.OrderedDicted collections.MutableSeteseguire il sollevamento pesante.)
import collections
class OrderedSet(collections.OrderedDict, collections.MutableSet):
def update(self, *args, **kwargs):
if kwargs:
raise TypeError("update() takes no keyword arguments")
for s in args:
for e in s:
self.add(e)
def add(self, elem):
self[elem] = None
def discard(self, elem):
self.pop(elem, None)
def __le__(self, other):
return all(e in other for e in self)
def __lt__(self, other):
return self <= other and self != other
def __ge__(self, other):
return all(e in self for e in other)
def __gt__(self, other):
return self >= other and self != other
def __repr__(self):
return 'OrderedSet([%s])' % (', '.join(map(repr, self.keys())))
def __str__(self):
return '{%s}' % (', '.join(map(repr, self.keys())))
difference = __sub__
difference_update = __isub__
intersection = __and__
intersection_update = __iand__
issubset = __le__
issuperset = __ge__
symmetric_difference = __xor__
symmetric_difference_update = __ixor__
union = __or__
OrderedSetche sottoclassi OrderedDicte abc.Setquindi definire __len__, __iter__e __contains__.
collections, ma per il resto un buon suggerimento
OrderedSet([1,2,3])genera un errore di tipo. Come funziona il costruttore? Esempio di utilizzo mancante.
La risposta è no, ma puoi usare collections.OrderedDictdalla libreria standard di Python con solo chiavi (e valori come None) per lo stesso scopo.
Aggiornamento : A partire da Python 3.7 (e CPython 3.6), lo standard dictè garantito per preservare l'ordine ed è più performante di OrderedDict. (Per compatibilità con le versioni precedenti e soprattutto leggibilità, tuttavia, potresti voler continuare a utilizzare OrderedDict.)
Ecco un esempio di come utilizzare dictun set ordinato per filtrare gli articoli duplicati preservando l'ordine, emulando così un set ordinato. Usa il dictmetodo class fromkeys()per creare un dict, quindi chiedi semplicemente il keys()retro.
>>> keywords = ['foo', 'bar', 'bar', 'foo', 'baz', 'foo']
>>> list(dict.fromkeys(keywords))
['foo', 'bar', 'baz']
dict.fromkeys(). Ma in quel caso, l'ordine delle chiavi viene preservato solo nelle implementazioni di CPython 3.6+, quindi OrderedDictè una soluzione più portatile quando l'ordine conta.
keys = (1,2,3,1,2,1) list(OrderedDict.fromkeys(keys).keys())-> [1, 2, 3], python-3.7. Funziona.
dict, setin Python 3.7+ purtroppo non si conserva l'ordine.
Posso farti meglio di un OrderedSet: boltons ha un Python puro, compatibile 2/3IndexedSet tipo che non è solo un insieme ordinato, ma supporta anche l'indicizzazione (come con le liste).
Semplicemente pip install boltons(o copia setutils.pynel tuo codebase), importa il IndexedSete:
>>> from boltons.setutils import IndexedSet
>>> x = IndexedSet(list(range(4)) + list(range(8)))
>>> x
IndexedSet([0, 1, 2, 3, 4, 5, 6, 7])
>>> x - set(range(2))
IndexedSet([2, 3, 4, 5, 6, 7])
>>> x[-1]
7
>>> fcr = IndexedSet('freecreditreport.com')
>>> ''.join(fcr[:fcr.index('.')])
'frecditpo'
Tutto è unico e mantenuto in ordine. Divulgazione completa: ho scritto il IndexedSet, ma ciò significa anche che puoi darmi bug in caso di problemi . :)
Mentre altri hanno sottolineato che non esiste un'implementazione integrata di un set di conservazione degli ordini di inserzione in Python (ancora), sento che a questa domanda manca una risposta che afferma cosa si può trovare su PyPI .
Ci sono i pacchetti:
Alcune di queste implementazioni si basano sulla ricetta pubblicata da Raymond Hettinger su ActiveState che è menzionata anche in altre risposte qui.
my_set[5])remove(item)Entrambe le implementazioni hanno O (1) per add(item)e __contains__(item)( item in my_set).
set.unionnon ci lavorano, anche se eredita collections.abc.Set.
OrderedSetora supportaremove
Se stai usando il set ordinato per mantenere un ordine ordinato, considera l'utilizzo di un'implementazione del set ordinato da PyPI. Il modulo sortcontainers fornisce un SortedSet proprio per questo scopo. Alcuni vantaggi: pure-Python, implementazioni fast-as-C, copertura del test unitario al 100%, ore di stress test.
L'installazione da PyPI è semplice con pip:
pip install sortedcontainers
Si noti che se non è possibile pip install, è sufficiente estrarre i file sortlist.py e sortset.py dal repository open source .
Una volta installato puoi semplicemente:
from sortedcontainers import SortedSet
help(SortedSet)
Il modulo dei contenitori ordinati mantiene anche un confronto delle prestazioni con diverse implementazioni alternative.
Per il commento che ha chiesto del tipo di dati bag di Python, c'è in alternativa un tipo di dati SortedList che può essere utilizzato per implementare in modo efficiente un bag.
SortedSetclasse lì richiede che i membri siano comparabili e hash.
sete frozensetrichiedono anche elementi per essere hash. Il vincolo comparabile è l'aggiunta per SortedSet, ma è anche un vincolo ovvio.
Nel caso in cui stai già utilizzando i panda nel tuo codice, il suo Indexoggetto si comporta in modo simile a un set ordinato, come mostrato in questo articolo .
Esempi dall'articolo:
indA = pd.Index([1, 3, 5, 7, 9])
indB = pd.Index([2, 3, 5, 7, 11])
indA & indB # intersection
indA | indB # union
indA - indB # difference
indA ^ indB # symmetric difference
indA.difference(indB), il segno meno esegue la sottrazione standard
Un po 'in ritardo al gioco, ma ho scritto una classe setlistcome parte di collections-extendedciò che implementa completamente sia SequenceeSet
>>> from collections_extended import setlist
>>> sl = setlist('abracadabra')
>>> sl
setlist(('a', 'b', 'r', 'c', 'd'))
>>> sl[3]
'c'
>>> sl[-1]
'd'
>>> 'r' in sl # testing for inclusion is fast
True
>>> sl.index('d') # so is finding the index of an element
4
>>> sl.insert(1, 'd') # inserting an element already in raises a ValueError
ValueError
>>> sl.index('d')
4
GitHub: https://github.com/mlenzen/collections-extended
Documentazione: http://collections-extended.lenzm.net/en/latest/
Non ce n'è OrderedSetnella biblioteca ufficiale. Faccio un completo cheatsheet di tutta la struttura dei dati per riferimento.
DataStructure = {
'Collections': {
'Map': [
('dict', 'OrderDict', 'defaultdict'),
('chainmap', 'types.MappingProxyType')
],
'Set': [('set', 'frozenset'), {'multiset': 'collection.Counter'}]
},
'Sequence': {
'Basic': ['list', 'tuple', 'iterator']
},
'Algorithm': {
'Priority': ['heapq', 'queue.PriorityQueue'],
'Queue': ['queue.Queue', 'multiprocessing.Queue'],
'Stack': ['collection.deque', 'queue.LifeQueue']
},
'text_sequence': ['str', 'byte', 'bytearray']
}
Il pacchetto ParallelRegression fornisce una classe set ordinata setList () che è più completa di metodo rispetto alle opzioni basate sulla ricetta ActiveState. Supporta tutti i metodi disponibili per gli elenchi e la maggior parte se non tutti i metodi disponibili per gli insiemi.
Come menzionano altre risposte, come per Python 3.7+, il dict è ordinato per definizione. Invece di sottoclasse OrderedDictpossiamo sottoclassare abc.collections.MutableSeto typing.MutableSetusare le chiavi del dict per memorizzare i nostri valori.
class OrderedSet(typing.MutableSet[T]):
"""A set that preserves insertion order by internally using a dict."""
def __init__(self, iterable: t.Iterator[T]):
self._d = dict.fromkeys(iterable)
def add(self, x: T) -> None:
self._d[x] = None
def discard(self, x: T) -> None:
self._d.pop(x)
def __contains__(self, x: object) -> bool:
return self._d.__contains__(x)
def __len__(self) -> int:
return self._d.__len__()
def __iter__(self) -> t.Iterator[T]:
return self._d.__iter__()
Quindi solo:
x = OrderedSet([1, 2, -1, "bar"])
x.add(0)
assert list(x) == [1, 2, -1, "bar", 0]
Ho inserito questo codice in una piccola libreria , quindi chiunque può pip installfarlo.
Per molti scopi sarà sufficiente semplicemente chiamare ordinati. Per esempio
>>> s = set([0, 1, 2, 99, 4, 40, 3, 20, 24, 100, 60])
>>> sorted(s)
[0, 1, 2, 3, 4, 20, 24, 40, 60, 99, 100]
Se lo utilizzerai ripetutamente, si verificherà un sovraccarico chiamando la funzione ordinata, quindi potresti voler salvare l'elenco risultante, a condizione che tu abbia finito di cambiare il set. Se è necessario mantenere elementi unici e ordinati, sono d'accordo con il suggerimento di utilizzare OrderedDict da raccolte con un valore arbitrario come Nessuno.
Quindi avevo anche un piccolo elenco in cui avevo chiaramente la possibilità di introdurre valori non univoci.
Ho cercato l'esistenza di un elenco univoco di qualche tipo, ma poi ho capito che testare l'esistenza dell'elemento prima di aggiungerlo funziona bene.
if(not new_element in my_list):
my_list.append(new_element)
Non so se ci sono avvertimenti in questo semplice approccio, ma risolve il mio problema.