web-dev-qa-db-fra.com

Est-ce que Python a un ensemble ordonné?

Python a un dictionnaire ordonné . Qu'en est-il d'un ensemble commandé?

406
Casebash

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
195
Casebash

Un ensemble ordonné est fonctionnellement un cas particulier d'un dictionnaire ordonné.

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__)
_
132
Stephan202

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 . :)

37
Mahmoud Hashemi

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 dictfromkeys() 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']
_
36
jrc

Implémentations sur PyPI

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

différences critiques:

  • ordonné-set (version 1.1)
    • avantage: O(1) pour les recherches par index (par exemple my_set[5])
    • inconvénient: remove(item) non implémenté
  • oset (version 0.1.3)
    • avantage: O(1) pour remove(item)
    • inconvénient: apparemment O(n) pour les recherches par index

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.

34
Daniel K

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.

16
GrantJ

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 .

7
Berislav Lopac

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']
}
6
Algebra

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/

PyPI: https://pypi.python.org/pypi/collections-extended

6
Michael Lenzen

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.

3
RichardB

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.

2
hwrd