Python有一个有序集吗?

Cas*_*ash 430 python set

Python有一个有序的字典.订购套装怎么样?

Cas*_*ash 197

有一个有序集(可能的新链接)配方,这是从Python 2文档中引用的.这在Py2.6或更高版本以及3.0或更高版本上运行,无需任何修改.除了初始化应该用列表完成之外,接口几乎与普通集完全相同.

OrderedSet([1, 2, 3])
Run Code Online (Sandbox Code Playgroud)

这是一个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
Run Code Online (Sandbox Code Playgroud)

  • 界面与普通的设置对象不完全相同,缺少许多基本方法,如`update`,`union`,`intersection`. (43认同)
  • 我很确定你不允许在同一个类中使用两个名为`union`的方法.最后一个将"赢",第一个将无法在运行时存在.这是因为`OrderedSet.union`(没有parens)必须引用*single*对象. (7认同)
  • 我选择了自己的答案,因为文档中的引用使其接近官方答案 (6认同)
  • 仅供参考,我注意到[这个答案中引用的配方]的[略微修改版本](https://github.com/LuminosoInsight/ordered-set)(http://code.activestate.com/recipes/576694/ )[已添加到PyPi](https://github.com/LuminosoInsight/ordered-set)为"ordered-set" (5认同)
  • 还有"orderedset"包,它基于相同的配方,但在Cython中实现 - https://pypi.python.org/pypi/orderedset. (2认同)

Ste*_*202 137

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

字典的键是唯一的.因此,如果忽略有序字典中的值(例如通过分配它们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 = 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__)
Run Code Online (Sandbox Code Playgroud)

  • 一个有用的近似用于几个目的,但它没有很好的设置操作. (36认同)
  • 执行 `OrderedSet([1,2,3])` 会引发 TypeError。构造函数是如何工作的?缺少用法示例。 (5认同)
  • 这是事实,但结果会浪费很多空间,导致性能欠佳. (4认同)
  • 这个答案需要重写为:(1)支持使用元组列表进行初始化,(2)通过组合而不是继承使用“dict”(因为它现在是有序的),以及(3)使用“collections.abc.MutableSet” `。 (4认同)
  • 增加项; collections.OrderedDict也可以在python 2.7中使用. (3认同)

jrc*_*jrc 49

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

以下是如何使用dict有序集来过滤掉重复项目同时保留顺序的示例:

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

>>> list(dict.fromkeys(keywords).keys())
['foo', 'bar', 'baz']
Run Code Online (Sandbox Code Playgroud)

  • @user474491 与 `dict` 不同,Python 3.7+ 中的 `set` 不幸的是不保留顺序。 (13认同)
  • 链接的答案说“dict”保留 **_insertion_** 顺序,但在对其内容进行操作后不一定保留顺序。它还指出了 `OrderedDict` 具有而 `dict` 没有的几个功能。`dict` 可能足以解决这个问题中提出的具体问题,但不应认为 `dict` 对保留插入顺序的支持意味着它是 `OrderedDict` 的 **_complete_** 替代品。 (11认同)
  • @AnwarHossain `keys = (1,2,3,1,2,1)` `list(OrderedDict.fromkeys(keys).keys())` -&gt; `[1, 2, 3]`,python-3.7。有用。 (5认同)
  • @DavidEhrmann 继续在同一链接上进一步阅读:“2017 年 12 月更新:Python 3.7 保证保留插入顺序的字典” (5认同)
  • 我们是否可以推断 Python 3.7+ 中的 Set 也保留顺序? (4认同)
  • 也许值得一提的是,这也可以(更快)与vanilla`dict.fromkeys()`一起工作.但在这种情况下,密钥顺序仅保留在CPython 3.6+实现中,因此当订单很重要时,"OrderedDict"是一种更便携的解决方案. (3认同)
  • 这回答了实际的问题,而不是直接跳到解决方法中。 (2认同)
  • `dict` *不*保证保持顺序。从链接中,“此新实现的顺序保留方面被视为实现细节,不应依赖。” (2认同)

Mah*_*emi 38

我可以比OrderedSet做得更好:boltons有一个纯Python,2/3兼容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'
Run Code Online (Sandbox Code Playgroud)

一切都是独特的,并保持有序.完全披露:我写了IndexedSet,但这也意味着如果有任何问题你可以告诉我.:)


Dan*_*l K 36

关于PyPI的实现

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

据我所知,目前有:

两种实现都基于Raymond Hettinger发布给ActiveState配方,这也在其他答案中提到.我已经检查了两个并确定了以下内容

关键差异:

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

两种实现都有O(1)for add(item)__contains__(item)(item in my_set).

遗憾的是,这两种实现都没有基于方法的集合操作,例如set1.union(set2)- >您必须使用基于运算符的表单set1 | set2.有关set操作方法及其基于运算符的等效项的完整列表,请参阅Set Objects上Python文档.

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

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

  • `OrderedSet`现在支持[`remove`](http://orderedset.readthedocs.org/en/latest/orderedset.html#orderedset.OrderedSet.remove) (3认同)
  • 新的竞争者是[collections_extended.setlist](http://stackoverflow.com/a/28052907/1031434).像`set.union`这样的函数虽然它继承了`collections.abc.Set`,但它不起作用. (2认同)

Gra*_*ntJ 17

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

使用pip从PyPI轻松安装:

pip install sortedcontainers
Run Code Online (Sandbox Code Playgroud)

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

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

from sortedcontainers import SortedSet
help(SortedSet)
Run Code Online (Sandbox Code Playgroud)

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

对于询问Python的数据包数据类型的注释,可以选择使用SortedList数据类型来有效地实现包.

  • @gsnedders内置的`set`和`frozenset`也需要元素可以清除.可比约束是`SortedSet`的加法,但它也是一个明显的约束. (4认同)
  • 顾名思义,这并不能维持秩序。它只是 sorted(set([sequence])) 哪个更好? (2认同)
  • @GrantJ - 是维护 _insertion_ order 还是 _sort_ order 之间的区别。大多数其他答案都与插入顺序有关。我认为您已经根据第一句话意识到了这一点,但这可能是 ldmtwo 所说的。 (2认同)

Ber*_*pac 8

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

  • 值得注意的是,“pd.Index”允许重复元素,而实际的 Python“set”中不会出现这种情况。 (3认同)

Alg*_*bra 8

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']
}
Run Code Online (Sandbox Code Playgroud)


bus*_*win 7

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

import itertools
import typing

T = typing.TypeVar("T")

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

    def __init__(self, iterable: typing.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, None)

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

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

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

    def __str__(self):
        return f"{{{', '.join(str(i) for i in self)}}}"

    def __repr__(self):
        return f"<OrderedSet {self}>"
Run Code Online (Sandbox Code Playgroud)

那么只需:

x = OrderedSet([1, 2, -1, "bar"])
x.add(0)
assert list(x) == [1, 2, -1, "bar", 0]
Run Code Online (Sandbox Code Playgroud)

我在一个小库中添加了此代码,并进行了一些测试,因此任何人都可以使用pip install它。

  • 不要按原样使用这个。`discard` 永远不应该引发 `KeyError`。另请注意,这并没有提供明智的“__repr__” (2认同)

小智 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
Run Code Online (Sandbox Code Playgroud)

GitHub:https://github.com/mlenzen/collections-extended

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

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


Dav*_*ann 5

正如其他人所说,就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)   
Run Code Online (Sandbox Code Playgroud)

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