tags:

views:

2140

answers:

2

Python has an ordered dictionary, what about an ordered set?

This question previously had a warning that there was going to be a self-answer.

+14  A: 

There is an ordered set recipe for this which is referred to from the Python Documentation. This runs on Py2.6 or later and 3.0 or later without any modifications. The interface is exactly the same as a normal set, except that initialisation should be done with a list.

OrderedSet([1, 2, 3])
Casebash
I selected this answer because the reference from the documentation makes this close to an official answer. Stephan202 answer is informative as well, so upvote it!
Casebash
+17  A: 

An ordered set is functionally a special case of an ordered dictionary.

The keys of a dictionary are unique. Thus, if one disregards the values in an ordered dictionary (e.g. by assigning them None), then one has essentially an ordered set.

As of Python 3.1 there is collections.OrderedDict. The following is an example implementation of an OrderedSet. (Note that only few methods need to be defined or overridden: collections.OrderedDict and collections.MutableSet do the heavy lifting.)

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__)
Stephan202
A useful approximation for several purposes, but it doesn't have the nice set operations.
Casebash
@Casebash: yes, one may want to define a class `OrderedSet` which subclasses `OrderedDict` and `abc.Set` and then define `__len__`, `__iter__` and `__contains__`.
Stephan202
@Stephan202: Regrettably, the collection ABCs live in `collections`, but otherwise a good suggestion
kaizer.se
@kaizer.se: right you are. I now posted an example implementation. Turns out my previous comment was not completely correct, but the posted code should speak for itself.
Stephan202