Python是否有序集?


476

Python有一个有序的字典。有序套呢?


18
相反,一袋东西呢?(无序且不唯一)
2011年

19
@wim collections.Counter是Python的包。
flornquake 2013年

1
如果两次添加某物怎么办?职位应该是什么?
麦凯

2
@McKay-如果要遵循collections.OrderDict的行为,它将仍处于初始添加的位置
wojtow

Answers:


206

为此,有一个有序的设置(可能的新链接)配方,可从Python 2文档中引用。无需修改即可在Py2.6或更高版本以及3.0或更高版本上运行。该接口几乎与普通集合完全相同,不同之处在于初始化应使用列表进行。

OrderedSet([1, 2, 3])

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

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

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

6
我选择了自己的答案,因为文档中的参考使该答案接近正式答案
Casebash 2010年

49
该接口是不完全一样的常规集合对象,许多基本方法缺失,如updateunionintersection
xApple 2012年

5
仅供参考,我注意到此答案中引用配方略有修改的版本已作为“有序集” 添加到PyPi
Geoffrey Hing

7
我很确定您不允许union在同一个类中同时调用两个方法。最后一个将“获胜”,第一个将在运行时不存在。这是因为OrderedSet.union(无括号)必须引用单个对象。
凯文

3
还有它是基于相同的配方,但用Cython实现“orderedset”包- pypi.python.org/pypi/orderedset
mbdevpl '16

149

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

字典的键是唯一的。因此,如果人们不理会有序字典中的值(例如,通过分配它们None),那么实质上就是一个有序集合。

对于Python 3.1的存在collections.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 = __sub__ 
    difference_update = __isub__
    intersection = __and__
    intersection_update = __iand__
    issubset = __le__
    issuperset = __ge__
    symmetric_difference = __xor__
    symmetric_difference_update = __ixor__
    union = __or__

1
@Casebash:是的,有可能要定义一个类OrderedSet,其子类OrderedDictabc.Set再定义__len____iter____contains__
Stephan202

1
@ Stephan202:遗憾的是,集合ABC居住在collections,但除此之外还有个不错的建议
u0b34a0f6ae 2009年

4
的确如此,但是结果确实浪费了很多空间,从而导致性能欠佳。
Daniel Kats 2012年

3
增加项; 在python 2.7中也可以使用collections.OrderedDict。
Nurbldoff

2
这样做OrderedSet([1,2,3])会引发TypeError。构造函数甚至如何工作?缺少用法示例。
xApple

89

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

更新:从Python 3.7(和CPython 3.6)开始,标准dict可以保证保持顺序,并且比更具性能OrderedDict。(但是,为了向后兼容,尤其是为了可读性,您可能希望继续使用OrderedDict。)

这是一个示例,该示例说明如何dict在保留订单的同时用作有序集来过滤出重复项,从而模拟有序集。使用dictclass方法fromkeys()创建字典,然后简单地要求它keys()

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

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

4
也许值得一提的是,这也适用于香草dict.fromkeys()。但是在那种情况下,键顺序仅保留在CPython 3.6+实现中,因此OrderedDict当顺序很重要时,则是更可移植的解决方案。
jez

1
如果值不是字符串,则将不起作用
Anwar Hossain

4
@AnwarHossain keys = (1,2,3,1,2,1) list(OrderedDict.fromkeys(keys).keys())-> [1, 2, 3],python-3.7。有用。
raratiru

1
我们也可以推断Python 3.7+中的Set保留顺序吗?
user474491

2
@ user474491与Python 不同dictset不幸的是,在3.7+中,它不会保留顺序。
CZ

39

我可以做得比OrderedSet更好:bollton具有2/3兼容的纯Python IndexedSet类型,该类型不仅是有序集合,而且还支持索引编制(与列表一样)。

简单地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,但是这也意味着如果有任何问题,您可以给我个麻烦。:)


39

在PyPI上的实现

尽管其他人指出,Python中还没有内置的插入顺序保留集实现(但是),但我感到这个问题缺少一个答案,该答案指出了在PyPI上可以找到的内容。

有软件包:

其中一些实现是基于Raymond Hettinger提交给ActiveState配方的,在其他答案中也提到了该配方

一些差异

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

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


2
一个新的竞争者是collections_extended.setlist。像set.union这样的函数即使在继承时也不会起作用collections.abc.Set
timdiels '16

3
OrderedSet现在支持remove
warvariuc '16

17

如果您使用排序集来维护排序顺序,请考虑使用PyPI中的排序集实现。该sortedcontainers模块提供了一个SortedSet的只是这个目的。一些好处:纯Python,快速C实现,100%单元测试覆盖率,数小时的压力测试。

通过pip从PyPI安装很容易:

pip install sortedcontainers

请注意,如果不能pip install,则只需从开源存储库中下拉sortedlist.py和sortedset.py文件。

安装完成后,您可以:

from sortedcontainers import SortedSet
help(SortedSet)

sortedcontainers模块还与几种替代实现保持性能比较

对于询问Python的bag数据类型的评论,还有一种SortedList数据类型,可用于有效地实现bag。


注意那里的SortedSet类要求成员是可比较的并且是可哈希的。
gsnedders 2014年

4
@gsnedders内置函数setfrozenset还要求元素是可哈希的。可比较的约束是的加法SortedSet,但也是显而易见的约束。
gotgenes 2015年

2
顾名思义,这并不维护顺序。排序更好(set([sequence]))更好吗?
ldmtwo

@ldmtwo我不确定您指的是哪一个,只是要清楚一点,作为Sorted Containers一部分的SortedSet确实会保持排序顺序。
GrantJ

2
@GrantJ-它是保持插入顺序还是排序顺序之间的区别。其他大多数答案都与插入顺序有关。我想您已经根据第一句话意识到了这一点,但这可能就是ldmtwo所说的。
贾斯汀

8

如果您已经在代码中使用了pandas,则其Index对象的行为就很像有序集,如本文中所示。

文章中的示例:

indA = pd.Index([1, 3, 5, 7, 9])
indB = pd.Index([2, 3, 5, 7, 11])

indA & indB  # intersection
indA | indB  # union
indA - indB  # difference
indA ^ indB  # symmetric difference

您可以在此答案中包含一个示例吗?链接可能会在一段时间后断开。
Alechan

1
对于集合之间的差异,您实际上需要使用indA.difference(indB),负号执行标准减法
gg349

7

太迟了一点,但我已经写了一类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


7

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

3

所述ParallelRegression包提供了SETLIST()有序集类,它是多个方法完成比基于ActiveState的配方的选项。它支持可用于列表的所有方法以及大多数(如果不是全部)可用于集合的方法。


2

正如其他答案所提到的,对于python 3.7+,该字典按定义排序。除了子类化之外,OrderedDict我们还可以子类化abc.collections.MutableSettyping.MutableSet使用dict的键存储值。

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]

我将此代码放在一个小的库中,所以任何人都可以pip install


-4

对于许多目的,只需调用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]

如果要重复使用此功能,则调用sorted函数会产生开销,因此,只要您完成更改集合的操作,就可能希望保存结果列表。如果您需要维护唯一元素并进行排序,那么我建议您使用具有任意值(例如无)的集合中的OrderedDict。


43
OrderedSet的目的是能够按添加到集合中的顺序获取项目。您的示例可能称为SortedSet ...
定期维护

-4

因此,我还有一个小清单,很明显可以引入非唯一值。

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

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

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


这种方法的主要问题是加法在O(n)中运行。这意味着随着列表的增加它变得越来越慢。Python的内置集非常擅长使元素添加速度更快。但是对于简单的用例,它确实可以工作!
Draconis '18年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.