Python有一个有序集吗?

时间:2009-10-31 10:12:08

标签: python set

Python有一个ordered dictionary。订购套装怎么样?

15 个答案:

答案 0 :(得分:194)

有一个ordered set(可能的new link)食谱,从Python 2 Documentation引用。这在Py2.6或更高版本以及3.0或更高版本上运行,无需任何修改。该接口几乎与普通集完全相同,只是初始化应该用列表完成。

OrderedSet([1, 2, 3])

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

@staticmethod
def union(*sets):
    union = OrderedSet()
    union.union(*sets)
    return union

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

答案 1 :(得分:130)

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

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

As of Python 3.1collections.OrderedDict。以下是OrderedSet的示例实现。 (请注意,只需要定义或覆盖的方法很少:collections.OrderedDictcollections.MutableSet执行繁重的工作。)

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__)

答案 2 :(得分:35)

我可以比OrderedSet做得更好:boltons有a pure-Python, 2/3-compatible IndexedSet type不仅是有序集,而且还支持索引(与列表一样)。

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

>>> 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,但这也意味着you can bug me if there are any issues。 :)

答案 3 :(得分:33)

PyPI上的实现

虽然其他人已经指出在Python中还没有内置的插入顺序保留集实现,但是我觉得这个问题缺少一个答案,说明{{{ 3}}

据我所知,目前有:

这两种实现都基于oset,这也在其他答案中提到。我已经检查了两个并确定了以下

关键差异:

  • 有序集(1.1版)
    • 优点:O(1)用于按索引查找(例如my_set[5]
    • 缺点:remove(item)未实施
  • oset(版本0.1.3)
    • 优势:remove(item)
    • 的O(1)
    • 缺点:显然O(n)用于按索引查找

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

不幸的是,这两种实现都没有基于方法的集合操作,如set1.union(set2) - &gt;您必须使用基于运算符的表单,例如set1 | set2。有关设置操作方法及其基于运算符的等效项的完整列表,请参阅recipe posted by Raymond Hettinger to ActiveState

我第一次使用有序集,直到我第一次使用remove(item)时,我的脚本被NotImplementedError崩溃了。因为到目前为止我从未使用过索引查找,所以我同时切换到了oset。

如果你了解PyPI的其他实现,请在评论中告诉我。

答案 4 :(得分:25)

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

更新:从Python 3.7(和CPython 3.6)开始,标准dictguaranteed to preserve order,性能比OrderedDict高。 (但是,出于可移植性和可读性,您可能希望继续使用OrderedDict。)

这里是一个示例,该示例说明如何使用dict作为有序集来在保留顺序的同时过滤出重复项,从而模拟有序集。使用dict类方法fromkeys()创建一个字典,然后索要keys()即可。

>>> keywords = ['foo', 'bar', 'bar', 'foo', 'baz', 'foo']

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

答案 5 :(得分:16)

如果您使用有序集来维护排序顺序,请考虑使用PyPI中的排序集实现。 sortedcontainers模块仅为此目的提供SortedSet。一些好处:纯Python,快速实施,100%单元测试覆盖,数小时的压力测试。

使用pip很容易从PyPI安装:

pip install sortedcontainers

请注意,如果您不能pip install,只需从open-source repository下拉sortedlist.py和sortedset.py文件。

安装完成后,您可以简单地:

from sortedcontainers import SortedSet
help(SortedSet)

sortedcontainers模块还维护一个performance comparison,其中有几个替代实现。

对于询问有关Python数据包数据类型的评论,还可以使用SortedList数据类型来有效地实现行李。

答案 6 :(得分:7)

如果您已在代码中使用pandas,则其Index对象的行为非常类似于有序集,如this article所示。

答案 7 :(得分:6)

游戏稍晚,但我写了setlist作为collections-extended的一部分,完全实现了SequenceSet

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

答案 8 :(得分:6)

官方图书馆中没有OrderedSet。 我制作了所有数据结构的详尽备忘单供您参考。

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

答案 9 :(得分:5)

出于许多目的,只需调用sorted就足够了。例如

>>> 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]

如果要重复使用它,则调用已排序的函数会产生开销,因此您可能希望保存结果列表,只要您完成更改集即可。如果您需要维护唯一元素并进行排序,我同意使用OrderedDict从集合中使用任意值(如None)的建议。

答案 10 :(得分:5)

正如其他答案所述,对于python 3.7+,该字典按定义排序。不用子类OrderedDict的子类,我们可以使用字典的键子子类来存储abc.collections.MutableSettyping.MutableSet的子类。

class OrderedSet(typing.MutableSet[T]):
    """A set that preserves insertion order by internally using a dict."""

    def __init__(self, iterable: t.Iterator[T]):
        self._d = dict.fromkeys(iterable)

    def add(self, x: T) -> None:
        self._d[x] = None

    def discard(self, x: T) -> None:
        self._d.pop(x)

    def __contains__(self, x: object) -> bool:
        return self._d.__contains__(x)

    def __len__(self) -> int:
        return self._d.__len__()

    def __iter__(self) -> t.Iterator[T]:
        return self._d.__iter__()

然后:

x = OrderedSet([1, 2, -1, "bar"])
x.add(0)
assert list(x) == [1, 2, -1, "bar", 0]

this example,所以任何人都可以pip install

答案 11 :(得分:3)

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

答案 12 :(得分:2)

正如其他人所说的,OrderedDict是功能性有序集的超集,但是如果您需要一个用于与API交互的集,而不需要则需要它易变,OrderedDict.keys()实际上是实现abc.collections.Set

import random
from collections import OrderedDict, abc

a = list(range(0, 100))
random.shuffle(a)

# True
a == list(OrderedDict((i, 0) for i in a).keys())

# True
isinstance(OrderedDict().keys(), abc.Set)   

注意事项是不可改变的,必须像字典一样构建集合,但它很简单,并且仅使用内置函数。

答案 13 :(得分:-2)

所以我也有一个清单,很明显我可以引入非唯一值。

我搜索了某种唯一列表的存在,但是后来意识到在添加元素之前测试元素的存在就可以了。

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

我不知道这种简单方法是否有警告,但可以解决我的问题。

答案 14 :(得分:-5)

我相信有四种可能需要的排序:

  1. 按键排序
  2. 按价值订购(虽然我没有听说有人要求这个)
  3. 按修改时间排序
  4. 按加时间排序
  5. 我相信collections.OrderedDict会让你#4。或者你可以移除一个键并重新添加它,#3。

    对于#1,你可能应该检查一个红黑树或treap:

    红黑树的操作时间差异很小(因此对于交互式应用程序可能更好),但并不像平均值那样快(对批处理来说可能更好) - treaps不会经常重组自己它们平均速度很快,但是当它们进行重组时可能需要相当长的时间)。

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