Python a un dictionnaire ordonné . Qu'en est-il d'un ensemble commandé?
Il existe une recette ensemble ordonné (possible nouveau lien ) à laquelle il est fait référence à partir de Documentation Python 2 . Cela fonctionne sur Py2.6 ou plus tard et 3.0 ou plus tard sans aucune modification. L’interface est presque identique à celle d’un ensemble normal, sauf que l’initialisation doit être faite avec une liste.
OrderedSet([1, 2, 3])
C'est un MutableSet, donc la signature pour .union
ne correspond pas à celle de set, mais puisqu'elle inclut __or__
, il est facile d'ajouter quelque chose de similaire:
@staticmethod
def union(*sets):
union = OrderedSet()
union.union(*sets)
return union
def union(self, *sets):
for set in sets:
self |= set
Les clés d'un dictionnaire sont uniques. Ainsi, si l’on ignore les valeurs d’un dictionnaire ordonné (par exemple en les affectant None
), on dispose alors essentiellement d’un ensemble ordonné.
À partir de Python 3.1 il y a collections.OrderedDict
. Voici un exemple d'implémentation d'un OrderedSet. (Notez que seules quelques méthodes doivent être définies ou remplacées: _collections.OrderedDict
_ et collections.MutableSet
font le gros du travail.)
_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 = property(lambda self: self.__sub__)
difference_update = property(lambda self: self.__isub__)
intersection = property(lambda self: self.__and__)
intersection_update = property(lambda self: self.__iand__)
issubset = property(lambda self: self.__le__)
issuperset = property(lambda self: self.__ge__)
symmetric_difference = property(lambda self: self.__xor__)
symmetric_difference_update = property(lambda self: self.__ixor__)
union = property(lambda self: self.__or__)
_
Je peux vous faire mieux qu'un OrderedSet: boltons has type IndexedSet
[pure-Python, compatible 2/3] qui est non seulement un ensemble ordonné, mais prend également en charge l'indexation (comme avec listes).
Simplement pip install boltons
(ou copiez setutils.py
dans votre base de code), importez la IndexedSet
et:
>>> 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'
Tout est unique et maintenu en ordre. Divulgation complète: j'ai écrit le IndexedSet
, mais cela signifie aussi vous pouvez me déranger s'il y a des problèmes . :)
La réponse est non, mais vous pouvez utiliser collections.OrderedDict
à partir de la bibliothèque standard Python avec uniquement des clés (et des valeurs telles que None
) aux mêmes fins.
Mise à jour : À partir de Python 3.7 (et CPython 3.6), le standard dict
est il est garanti de préserver ordre et est plus performant que OrderedDict
. (Cependant, pour des raisons de portabilité et de lisibilité, vous pouvez continuer à utiliser OrderedDict
.)
Voici un exemple d'utilisation de dict
en tant qu'ensemble ordonné pour filtrer les éléments en double tout en préservant l'ordre afin d'émuler un ensemble ordonné. Utilisez la méthode de classe dict
fromkeys()
pour créer un dict, puis demandez simplement le retour de keys()
.
_>>> keywords = ['foo', 'bar', 'bar', 'foo', 'baz', 'foo']
>>> list(dict.fromkeys(keywords).keys())
['foo', 'bar', 'baz']
_
Tandis que d'autres ont souligné qu'il n'existait pas d'implémentation intégrée d'un ensemble de préservation d'ordre d'ordre d'insertion dans Python (encore), j'ai le sentiment qu'il manque à cette question une réponse qui indique ce qu'il y a à trouver. sur PyPI .
À ma connaissance, il y a actuellement:
Les deux implémentations sont basées sur le recette publiée par Raymond Hettinger dans ActiveState , ce qui est également mentionné dans d'autres réponses ici. J'ai vérifié les deux et identifié les suivantes
my_set[5]
)remove(item)
non implémentéremove(item)
Les deux implémentations ont O(1) pour add(item)
et __contains__(item)
(item in my_set
).
Malheureusement, aucune des deux implémentations n'a d'opération d'ensemble basée sur une méthode comme set1.union(set2)
-> Vous devez utiliser le formulaire basé sur un opérateur tel que set1 | set2
à la place. Reportez-vous à la documentation Python sur Set Objects pour obtenir une liste complète des méthodes d'opération set et de leurs équivalents basés sur opérateur.
J'y suis d'abord allé avec orders-set jusqu'à ce que j'utilise remove(item)
pour la première fois et que mon script se bloque avec un NotImplementedError
. N'ayant jamais eu recours à la recherche index par index, je suis passé à oset.
Si vous connaissez d'autres implémentations sur PyPI, faites-le moi savoir dans les commentaires.
Si vous utilisez l'ensemble ordonné pour maintenir un ordre trié, envisagez d'utiliser une implémentation d'ensemble triée à partir de PyPI. Le module containerscontainers fournit un SortedSet uniquement à cette fin. Quelques avantages: Python pur, implémentations rapides, couverture à 100% des tests unitaires, heures de tests de contrainte.
L'installation à partir de PyPI est facile avec pip:
pip install sortedcontainers
Notez que si vous ne pouvez pas utiliser pip install
, extrayez simplement les fichiers sortlistlist.py et sortedset.py à partir du référentiel open-source .
Une fois installé, vous pouvez simplement:
from sortedcontainers import SortedSet
help(SortedSet)
Le module de conteneurs triés conserve également un comparaison de performances avec plusieurs implémentations alternatives.
Pour le commentaire portant sur le type de données bag de Python, il existe également un type SortedList qui peut être utilisé pour implémenter efficacement un bagage.
Si vous utilisez déjà pandas dans votre code, son objet Index
se comporte plutôt comme un ensemble ordonné, comme indiqué dans cet article .
Il n'y a pas de OrderedSet
dans la bibliothèque officielle. Je fais une feuille de triche exhaustive de toutes les structures de données pour votre référence.
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']
}
Un peu tard dans le jeu, mais j'ai écrit une classe setlist
dans le cadre de collections-extended
qui implémente pleinement les fonctions Sequence
et Set
>>> 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
Documentation: http://collections-extended.lenzm.net/en/latest/
Le package ParallelRegression fournit une classe d'ensemble setList () qui est plus complète en méthodes que les options basées sur la recette ActiveState. Il prend en charge toutes les méthodes disponibles pour les listes et la plupart sinon toutes les méthodes disponibles pour les ensembles.
Pour de nombreuses raisons, un simple appel trié suffira. Par exemple
>>> 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]
Si vous comptez utiliser cela à plusieurs reprises, l'appel de la fonction triée occasionnera une surcharge. Vous voudrez peut-être sauvegarder la liste obtenue, tant que vous aurez fini de modifier l'ensemble. Si vous devez conserver des éléments uniques et triés, je suis d'accord avec la suggestion d'utiliser OrderedDict à partir de collections avec une valeur arbitraire telle que Aucune.