为什么 Python 集合不保留插入顺序?

2023-11-25

我最近惊讶地发现,虽然在 Python 3.7+ 中字典可以保证保留插入顺序,但集合却不能:

>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> d
{'a': 1, 'b': 2, 'c': 3}
>>> d['d'] = 4
>>> d
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
>>> s = {'a', 'b', 'c'}
>>> s
{'b', 'a', 'c'}
>>> s.add('d')
>>> s
{'d', 'b', 'a', 'c'}

造成这种差异的原因是什么?导致 Python 团队更改 dict 实现的相同效率改进是否也不适用于集合?

我不是在寻找指向有序集实现的指针或使用字典作为集合的替代品的方法。我只是想知道为什么 Python 团队没有在对字典这样做的同时让内置集合保持顺序。


集合和字典针对不同的用例进行了优化。集合的主要用途是快速成员资格测试,这是与顺序无关的。对于字典来说,查找的成本是最关键的操作,并且密钥更有可能存在。对于集合,事先无法知道元素是否存在,因此集合实现需要针对已找到和未找到的情况进行优化。此外,对常见集合操作(​​例如并集和交集)的一些优化使得很难在不降低性能的情况下保留集合排序。

虽然这两种数据结构都是基于哈希的,但一个常见的误解是集合只是作为具有空值的字典来实现。甚至beforeCPython 3.6 中的紧凑 dict 实现中,set 和 dict 实现已经存在显着差异,几乎没有代码重用。例如,字典使用随机探测,但集合使用线性探测和开放寻址的组合来改进缓存局部性。初始线性探头(默认9 steps在 CPython 中)将检查一系列相邻的密钥/哈希对,通过降低哈希冲突处理的成本来提高性能 - 连续的内存访问比分散的探针更便宜。

  • dictobject.c - main, v3.5.9
  • setobject.c - main, v3.5.9
  • 问题18771- 更改集以减少 Python 3.4 中集合对象的哈希冲突成本。

这将是possible理论上将 CPython 的 set 实现更改为类似于紧凑字典,但实际上存在缺陷,并且著名的核心开发人员反对进行这样的更改。

集合保持无序。 (为什么?使用模式不同。而且实现也不同。)

吉多·范罗苏姆

集使用不同的算法,该算法无法修改以保留插入顺序。 如果需要订单,按组操作就会失去灵活性和优化性。集合数学是根据无序集合定义的。简而言之,集合排序不会在不久的将来出现。

雷蒙德·海廷格

关于是否压缩 3.7 集以及为什么决定反对的详细讨论可以在 python-dev 邮件列表中找到。

总结起来,要点是:不同的使用模式(插入排序字典如 **kwargs 是useful,对于集合来说更是如此),压缩集合的空间节省不太重要(因为只有键+哈希数组来致密,而不是键+哈希+值数组),并且集合当前使用的前述线性探测优化是不兼容的具有紧凑的实施方式。

我将在下面转载雷蒙德的帖子,其中涵盖了最重要的要点。

2016 年 9 月 14 日下午 3:50,埃里克·斯诺 (Eric Snow) 写道:

然后,我将对集合做同样的事情。

除非我误会了,否则雷蒙德反对制作类似的作品 更改为设置。

这是正确的。以下是人们对这个主题的一些想法 开始狂奔。

  • 对于紧凑字典,节省的空间是净胜,因为索引消耗了额外的空间,并且过度分配了 键/值/哈希数组超出了改进后的偏移量 键/值/哈希数组的密度。然而对于套装来说,网很多 不太有利,因为我们仍然需要指数和过度分配 但只能通过仅增加三个中的两个来抵消空间成本 数组。换句话说,当你有 浪费了键、值和哈希的空间。如果你失去其中之一 第三,它不再具有吸引力。

  • 集合的使用模式与字典不同。前者有更多的命中或未命中查找。后者丢失钥匙的情况往往较少 查找。另外,对设置到设置操作的一些优化 很难在不影响的情况下保留集合顺序 表现。

  • 我寻求替代途径来提高设定性能。而不是紧凑(这并没有赢得太多空间,并且产生了成本) 额外的间接),我添加了线性探测以降低成本 冲突并提高缓存性能。这个改进是 与我提倡的压缩方法不兼容 字典。

  • 目前,字典上的排序副作用是无法保证的,因此开始坚持集合也是有序的还为时过早。 该文档已经链接到创建 OrderedSet 的方法(https://code.activestate.com/recipes/576694/)但似乎 吸收率几乎为零。另外,现在埃里克·斯诺(Eric Snow)给了我们一个 快速 OrderedDict,比以往任何时候都更容易构建 OrderedSet MutableSet 和 OrderedDict,但我再次没有观察到任何真正的 感兴趣,因为典型的逐组数据分析并不真正需要 或关心订购。同样,快速会员的主要用途 测试与顺序无关。

  • 也就是说,我确实认为有空间向 PyPI 添加替代集实现。特别是,有一些有趣的 可排序数据的特殊情况,其中可以进行设置到设置的操作 通过比较整个范围的键来加速(参见https://code.activestate.com/recipes/230113-implementation-of-sets-using-sorted-lists为起点)。 IIRC,PyPI 已经有类似集合的绽放代码 过滤器和布谷鸟散列。

  • 我知道 Python 核心接受主要代码块是令人兴奋的,但这不应该打开闸门 除非我们确定,否则对其他数据类型进行更多重大重写 这是有保证的。

——雷蒙德·海廷格

From [Python-Dev] Python 3.6 dict 变得紧凑并获得私有版本;并且关键字变得有序,2016 年 9 月。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 Python 集合不保留插入顺序? 的相关文章

  • 如何在 Windows 64 上安装 NumPy?

    NumPy 安装程序在注册表中找不到 python 路径 无法安装 需要 Python 2 5 版本 但在注册表中未找到该版本 OK 我必须修改注册表吗 我已经修改了 PATH 以指向Python25安装目录 我可以检查一下您使用的是什么安
  • Python 3 os.urandom

    在哪里可以找到完整的教程或文档os urandom 我需要获得一个随机 int 来从 80 个字符的字符串中选择一个字符 如果你只需要一个随机整数 你可以使用random randint a b 来自随机模块 http docs pytho
  • 如何在python 3.7中生成条形码

    我正在使用 python 3 7 为了生成条形码 我尝试使用安装 pyBarcode 库pip install pyBarcode 但它显示以下错误 找不到满足 pyBarcode 要求的版本 来自版本 找不到 pyBarcode 的匹配分
  • 使用 pygame 显示 unicode 符号

    我检查了其他答案 但不明白为什么我的代码错误地显示 This is what I currently see https i stack imgur com 8tNIK png 这是关于文本渲染的相关代码 font pygame font
  • 如何以“正确”的方式处理带有空字节的 Python unicode 字符串?

    Question PyWin32 似乎很乐意将 null 终止的 unicode 字符串作为返回值 我想以 正确 的方式处理这些字符串 假设我得到一个像这样的字符串 u C Users Guest MyFile asy x00 x00sy
  • opencv水印周围的轮廓

    我想在图像中的水印周围画一个框 我已经提取了水印并找到了轮廓 但是 不会在水印周围绘制轮廓 轮廓是在我的整个图像上绘制的 请帮我提供正确的代码 轮廓坐标的输出为 array 0 0 0 634 450 634 450 0 dtype int
  • Python 2.7 中的断言对我来说不起作用示例assertIn

    我的 Mac 上安装了 python 2 7 通过在终端中运行 python v 进行验证 当我尝试使用任何新的 2 7 断言方法时 我收到 AtributeError 我看过http docs python org 2 library u
  • pyspark 数据框中的自定义排序

    是否有推荐的方法在 pyspark 中实现分类数据的自定义排序 我理想地寻找 pandas 分类数据类型提供的功能 因此 给定一个数据集Speed列 可能的选项是 Super Fast Fast Medium Slow 我想实现适合上下文的
  • Python 中的流式传输管道

    我正在尝试使用 Python 将 vmstat 的输出转换为 CSV 文件 因此我使用类似的方法转换为 CSV 并将日期和时间添加为列 vmstat 5 python myscript py gt gt vmstat log 我遇到的问题是
  • Python3.0 - 标记化和取消标记化

    我正在使用类似于以下简化脚本的内容来解析较大文件中的 python 片段 import io import tokenize src foo bar src bytes src encode src io BytesIO src src l
  • 在 Windows 上使用 apache mod_wsgi 运行 Flask 应用程序时导入冲突

    我允许您询问我在 Windows 上使用您的 mod wsgi portage 托管 Flask 应用程序时遇到的问题 我有两个烧瓶应用程序 由于导入冲突 只有一个可以同时存在 IE 如果请求申请 1 我有回复 然后 如果我请求应用程序 2
  • pytest:同一接口的不同实现的可重用测试

    想象一下我已经实现了一个名为的实用程序 可能是一个类 Bar在一个模块中foo 并为其编写了以下测试 测试 foo py from foo import Bar as Implementation from pytest import ma
  • Python:IndexError:修改代码后列表索引超出范围

    我的代码应该提供以下格式的输出 我尝试修改代码 但我破坏了它 import pandas as pd from bs4 import BeautifulSoup as bs from selenium import webdriver im
  • ANTLR 获取并拆分词法分析器内容

    首先 对我的英语感到抱歉 我还在学习 我为我的框架编写 Python 模块 用于解析 CSS 文件 我尝试了 regex ply python 词法分析器和解析器 但我发现自己在 ANTLR 中 第一次尝试 我需要解析 CSS 文件中的注释
  • Anaconda 无法导入 ssl 但 Python 可以

    Anaconda 3 Jupyter笔记本无法导入ssl 但使用Atom终端导入ssl没有问题 我尝试在 Jupyter 笔记本中导入 ssl 但出现以下错误 C ProgramData Anaconda3 lib ssl py in
  • Python SSL X509:KEY_VALUES_MISMATCH

    Python HTTPS server from http server import HTTPServer SimpleHTTPRequestHandler import ssl https stackoverflow com a 408
  • 在 Django 查询中使用 .extra(select={...}) 引入的值上使用 .aggregate() ?

    我正在尝试计算玩家每周玩游戏的次数 如下所示 player game objects extra select week WEEK games game date aggregate count Count week 但姜戈抱怨说 Fiel
  • Django Admin 中的反向内联

    我有以下 2 个型号 现在我需要将模型 A 内联到模型 B 的页面上 模型 py class A models Model name models CharField max length 50 class B models Model n
  • 双击打开 ipython 笔记本

    相关文章 通过双击 osx 打开 ipython 笔记本 https stackoverflow com questions 16158893 open an ipython notebook via double click on osx
  • 从 pandas DataFrame 中删除少于 K 个连续 NaN

    我正在处理时间序列数据 我在从数据帧列中删除小于或等于阈值的连续 NaN 时遇到问题 我尝试查看一些链接 例如 标识连续 NaN 出现的位置以及计数 Pandas NaN 孔的游程长度 https stackoverflow com que

随机推荐