Python有一个有序集吗?

Does Python have an ordered set?

python有一个有序的字典。一套定购的怎么样?


这有一个有序的集合(可能是新的链接)方法,可以从python 2文档中引用。在PY2.6或更高版本以及3.0或更高版本上运行,无需任何修改。接口几乎与正常设置完全相同,只是初始化应该用一个列表来完成。

1
OrderedSet([1, 2, 3])

这是一个可变集,因此.union的签名与set的签名不匹配,但由于它包含__or__的内容,可以很容易地添加类似的内容:

1
2
3
4
5
6
7
8
9
@staticmethod
def union(*sets):
    union = OrderedSet()
    union.union(*sets)
    return union

def union(self, *sets):
    for set in sets:
        self |= set


有序集在功能上是有序字典的特例。

字典的键是唯一的。因此,如果忽略了有序字典中的值(例如,通过给它们指定None),那么基本上就有了有序集。

从python 3.1开始,有collections.OrderedDict。下面是orderedset的一个示例实现。(注意,只有很少的方法需要定义或重写:collections.OrderedDictcollections.MutableSet进行重载提升。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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__)


我可以为您做一个比有序集更好的:Boltons有一个纯python,2/3兼容的IndexedSet类型,它不仅是有序集,而且支持索引(与列表一样)。

只需pip install boltons(或将setutils.py复制到代码库中),导入IndexedSet和:

1
2
3
4
5
6
7
8
9
10
11
>>> 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'

一切都是独一无二的,井然有序。完全公开:我写了IndexedSet,但这也意味着如果有任何问题,你可以打扰我。:)


Pypi上的实现

虽然其他人已经指出,在python中还没有插入顺序保留集的内置实现,但我觉得这个问题缺少一个说明在pypi上可以找到什么的答案。

据我所知,目前有:

  • 有序集
  • OSET

这两种实现都基于RaymondHettinger发布给ActiveState的配方,其他答案也提到了这一点。我已经检查了两个并确定了以下内容

关键区别:

  • 有序集(1.1版)
    • 优势:O(1)用于按索引查找(如my_set[5])
    • 缺点:remove(item)未实施
  • OSET(0.1.3版)
    • 优势:o(1)用于remove(item)
    • 缺点:按索引查找显然是O(N)

两种实现都有用于add(item)__contains__(item)的O(1)(item in my_set)。

不幸的是,两种实现都没有基于方法的集合操作,比如set1.union(set2)->您必须使用基于运算符的表单,比如set1 | set2。有关集合操作方法及其基于运算符的等效方法的完整列表,请参见集合对象的python文档。

我第一次使用有序的集合,直到我第一次使用remove(item),它使我的脚本与NotImplementedError崩溃。因为到目前为止我还没有使用过按索引查找,所以我同时切换到了OSET。

如果您知道Pypi上的其他实现,请在注释中告诉我。


答案是否定的,但是您可以使用Python标准库中的collections.OrderedDict,仅使用键(和值作为None)来实现相同的目的。

更新:从python 3.7(和cpython 3.6)开始,标准的dict保证了订单的保存,并且比OrderedDict更具性能。(但是,对于可移植性和可读性,您可能希望继续使用OrderedDict。)

下面是一个示例,说明如何使用dict作为一个有序集,在保留顺序的同时过滤出重复项,从而模拟一个有序集。使用dict类方法fromkeys()创建一个dict,然后简单地请求keys()返回。

1
2
3
4
>>> keywords = ['foo', 'bar', 'bar', 'foo', 'baz', 'foo']

>>> list(dict.fromkeys(keywords).keys())
['foo', 'bar', 'baz']


如果您使用排序集来维护排序顺序,那么考虑使用来自PYPI的排序集实现。SortedContainers模块仅为此目的提供SortedSet。一些好处:纯Python、快速的as-c实现、100%的单元测试覆盖率、数小时的压力测试。

使用pip从pypi安装很容易:

1
pip install sortedcontainers

请注意,如果不能执行pip install,只需从开放源代码存储库中下拉sortedlist.py和sortedset.py文件即可。

安装后,您可以简单地:

1
2
from sortedcontainers import SortedSet
help(SortedSet)

SortedContainers模块还与几个可选实现保持性能比较。

对于询问关于python的bag数据类型的注释,也可以使用sortedList数据类型来高效地实现bag。


如果您已经在代码中使用了panda,那么它的Index对象的行为就相当于一个有序的集合,如本文所示。


官方图书馆里没有OrderedSet。我对所有的数据结构做了详尽的记录,供你参考。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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']
}

游戏稍微晚了一点,但我已经写了一个类setlist,作为collections-extended的一部分,它完全实现SequenceSet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> 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

文档:http://collections-extended.lenzm.net/en/latest/

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


对于许多目的来说,简单地调用sorted就足够了。例如

1
2
3
>>> 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]

如果您要重复使用它,那么调用排序函数会产生开销,因此您可能希望保存结果列表,只要您更改了集合。如果您需要维护唯一的元素并对其进行排序,我同意从具有任意值(如none)的集合中使用ordereddict的建议。


ParallelRegression包提供了一个setList()顺序集类,它比基于ActiveState配方的选项更完整。它支持所有可用于列表的方法,如果不是所有方法都可用于集合,则支持大多数方法。


所以我也有一个小列表,很明显我有可能引入非唯一的值。

我搜索了某种独特列表的存在性,但是后来意识到在添加元素之前测试元素的存在性是很好的。

1
2
if(not new_element in my_list):
    my_list.append(new_element)

我不知道这种简单的方法是否值得注意,但它解决了我的问题。


我相信有四种订购方式:

  • 按键排序
  • 按价值订购(不过我没有听说有人要这个)
  • 按修改时间排序
  • 按添加时间排序
  • 我相信collections.ordereddict会让你4。或者,您可以删除一个键并重新添加它,用于3。

    对于1,您可能应该查看红黑树或Treap:

    • http://pypi.python.org/pypi/bintrees/0.3.0
    • http://pypi.python.org/pypi/rbtree/
    • http://stromberg.dnsalias.org/~dstromberg/treap/

    红黑树在操作时间上的可变性很低(因此对于交互应用程序来说可能更好),但其速度不如平均水平上的Treaps(对于批处理来说可能更好——Treaps不会自我重组,通常会使其快速,但当它们进行重组时,可能需要相当长的时间)。

    这两种方法都是建立的数据结构,具有多种语言的实现。