162 votes

La conversion d'une liste en un ensemble modifie l'ordre des éléments.

Récemment, j'ai remarqué que lorsque je convertissais un fichier list a set l'ordre des éléments est modifié et est trié par caractère.

Prenons cet exemple :

x=[1,2,20,6,210]
print x 
# [1, 2, 20, 6, 210] # the order is same as initial order

set(x)
# set([1, 2, 20, 210, 6]) # in the set(x) output order is sorted

Mes questions sont -

  1. Pourquoi cela se produit-il ?
  2. Comment faire des opérations de set (notamment Set Difference) sans perdre l'ordre initial ?

149 votes

@KarlKnechtel - Oui "l'ordre est un concept sans signification pour les ensembles...en mathématiques" mais j'ai des problèmes du monde réel :)

1 votes

Sur CPython 3.6+ unique = list(dict.fromkeys([1, 2, 1]).keys()) . Cela fonctionne parce que dict s préserver l'ordre d'insertion maintenant.

169voto

Sven Marnach Points 133943
  1. A set est une structure de données non ordonnée, elle ne préserve donc pas l'ordre d'insertion.

  2. Cela dépend de vos besoins. Si vous avez une liste normale, et que vous voulez supprimer un ensemble d'éléments tout en préservant l'ordre de la liste, vous pouvez le faire avec une compréhension de liste :

    >>> a = [1, 2, 20, 6, 210]
    >>> b = set([6, 20, 1])
    >>> [x for x in a if x not in b]
    [2, 210]

    Si vous avez besoin d'une structure de données qui supporte à la fois tests d'adhésion rapides y préservation de l'ordre d'insertion vous pouvez utiliser les clés d'un dictionnaire Python, qui, à partir de Python 3.7, est garanti pour préserver l'ordre d'insertion :

    >>> a = dict.fromkeys([1, 2, 20, 6, 210])
    >>> b = dict.fromkeys([6, 20, 1])
    >>> dict.fromkeys(x for x in a if x not in b)
    {2: None, 210: None}

    b n'a pas vraiment besoin d'être ordonné ici - vous pourriez utiliser une set également. Notez que a.keys() - b.keys() renvoie la différence entre les ensembles sous la forme d'un set Il n'est donc pas possible de conserver l'ordre d'insertion.

    Dans les anciennes versions de Python, vous pouvez utiliser collections.OrderedDict à la place :

    >>> a = collections.OrderedDict.fromkeys([1, 2, 20, 6, 210])
    >>> b = collections.OrderedDict.fromkeys([6, 20, 1])
    >>> collections.OrderedDict.fromkeys(x for x in a if x not in b)
    OrderedDict([(2, None), (210, None)])

67voto

Tiger-222 Points 3180

Dans Python 3.6, set() maintenant devrait garder l'ordre, mais il existe une autre solution pour Python 2 et 3 :

>>> x = [1, 2, 20, 6, 210]
>>> sorted(set(x), key=x.index)
[1, 2, 20, 6, 210]

8 votes

Deux remarques concernant la préservation de l'ordre : seulement à partir de Python 3.6, et même là, c'est considéré comme un détail d'implémentation, donc ne comptez pas dessus. En dehors de cela, votre code est très inefficace car à chaque fois que x.index est appelé, une recherche linéaire est effectuée. Si la complexité quadratique vous convient, il n'y a aucune raison d'utiliser une recherche linéaire. set en premier lieu.

35 votes

@ThijsvanDien C'est faux, set() n'est pas ordonné dans Python 3.6, pas même comme un détail d'implémentation, vous pensez à dict s

0 votes

@Chris_Rands Je me suis trompé ; ils semblent être triés, plutôt que de conserver l'ordre d'insertion. Quoi qu'il en soit : détail d'implémentation.

24voto

lvella Points 3221

Pour répondre à votre première question, un ensemble est une structure de données optimisée pour les opérations sur les ensembles. Comme un ensemble mathématique, il n'impose ni ne maintient aucun ordre particulier des éléments. Le concept abstrait d'un ensemble n'impose pas d'ordre, et l'implémentation n'est donc pas tenue de le faire. Lorsque vous créez un ensemble à partir d'une liste, Python a la liberté de modifier l'ordre des éléments pour les besoins de l'implémentation interne qu'il utilise pour un ensemble, qui est capable d'effectuer des opérations d'ensemble efficacement.

19voto

Sana Points 427

Supprimer les doublons et préserver l'ordre par la fonction ci-dessous

def unique(sequence):
    seen = set()
    return [x for x in sequence if not (x in seen or seen.add(x))]

Comment supprimer les doublons d'une liste tout en préservant l'ordre en Python ?

0 votes

C'est exactement ce pour quoi j'utilisais set, et cela résout un des principaux problèmes de l'utilisation de set pour supprimer les doublons d'une liste : la perte de l'ordre original de la liste.

17voto

pylang Points 12013

En mathématiques, il y a fixe y ensembles ordonnés (osets).

  • set : un conteneur non ordonné d'éléments uniques (implémenté)
  • oset : un conteneur ordonné d'éléments uniques (NotImplemented)

En Python, seuls les ensembles sont directement implémentés. Nous pouvons émuler les osets avec des clés de dictée régulières ( 3.7+ ).

Étant donné que

a = [1, 2, 20, 6, 210, 2, 1]
b = {2, 6}

Código

oset = dict.fromkeys(a).keys()
# dict_keys([1, 2, 20, 6, 210])

Démo

Les répliques sont supprimées, l'ordre d'insertion est préservé.

list(oset)
# [1, 2, 20, 6, 210]

Opérations de type "set" sur les touches de dictée.

oset - b
# {1, 20, 210}

oset | b
# {1, 2, 5, 6, 20, 210}

oset & b
# {2, 6}

oset ^ b
# {1, 5, 20, 210}

Détails

Remarque : un non ordonné n'exclut pas les éléments ordonnés. Au contraire, le maintien de l'ordre n'est pas garanti. Exemple :

assert {1, 2, 3} == {2, 3, 1}                    # sets (order is ignored)

assert [1, 2, 3] != [2, 3, 1]                    # lists (order is guaranteed)

On peut être heureux de découvrir qu'un liste y multiset (mset) sont deux autres structures de données mathématiques fascinantes :

  • liste : un conteneur ordonné d'éléments qui permet les répliques (implémenté)
  • mset : un conteneur non ordonné d'éléments qui permet les répliques (NotImplemented)*.

Résumé

Container | Ordered | Unique | Implemented
----------|---------|--------|------------
set       |    n    |    y   |     y
oset      |    y    |    y   |     n
list      |    y    |    n   |     y
mset      |    n    |    n   |     n*  

*Un multiset peut être émulé indirectement avec collections.Counter() une cartographie de type dictée de multiplicités (comptes).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X