【问题标题】:How can I recreate Python 2's dictionary "ordering" in Python 3?如何在 Python 3 中重新创建 Python 2 的字典“排序”?
【发布时间】:2021-12-30 01:10:49
【问题描述】:

我正在尝试将 Python 2 字典迁移到 Python 3。

dict 的排序行为当然发生了变化:

  • Python 2:无序 - 但实际上在使用相同键值对的代码的后续调用之间似乎是确定性/一致的从我的角度来看是计算机科学领域)。

  • Python 3.7+:保证插入顺序。

我正在遍历字典的元素并将键值对添加到另一个结构中,这种方式是依赖于顺序的dict 值的函数。具体来说,我根据元素与某些地标(“最近的邻居”)的几何接近度来删除元素。如果顺序发生变化,则移除顺序发生变化,结果不一样。

我正在尝试重现 Python 2 对插入顺序的精确修改。这可以被认为是一个 python 函数或“行为”。那么,如何在 Python 3.7+ 中复制 Python 2 dict 排序?

注意事项:

  1. 当然,我可以在 Python 2(或 3.7)中使用 OrderedDict 并且经常这样做(并且应该在此处使用)。但是出于这个问题的目的,请不要建议它,或者不使用它是一个坏主意等。

  2. 我可以重新排序原始字典的键。这可能是解决难题的途径吗?一般来说,Python 2 可以在 Python 3 中“模拟”吗?

【问题讨论】:

  • 您要解决什么问题,或者这纯粹是出于您的好奇心?在什么情况下,现在订购的 dicts 是迁移的问题?
  • 注意字典在 Python 3.7+ 中仍然没有语义排序 - 例如,相等比较会忽略顺序。这感觉很像XY problem,你能提供导致你问这个问题的背景吗?
  • "我可以重新排序原始字典的键。" - 如 Python 2 代码中的字典?您可以这样做,但不能使用 OrderedDict,这似乎很奇怪。
  • "Python 2 dict ordering" 没有任何意义,因为没有 [你可以依赖的] 排序形式。因此,您似乎需要 Python 3+ 中的某种 UN 有序字典。好吧,您可以使用原始字典并根据从原始字典中提取的随机样本(键)构建一个新字典。但是为什么???
  • 比起任务本身,我更困惑于你认为或不认为可行的具体替代方案。

标签: python python-3.x python-2.7 dictionary


【解决方案1】:

我正在尝试重现 Python 2 对插入顺序的精确修改。这可以被认为是一个 python 函数或“行为”。那么,如何在 Python 3.7+ 中复制 Python 2 dict 排序?

你不能。

我可以重新排序原始字典的键。这可能是解决难题的途径吗?一般来说,Python 2 可以在 Python 3 中“模拟”吗?

没有。无论如何都要重新实现 Python 2 的 hashmap(或以新名称编译“旧”dict)。

Python 2 排序(以及一般的 hashmap 排序)是哈希函数、冲突解决算法和调整大小阈值(负载因子)的结果:

  • hashmap 基本上是一个桶数组,没有特殊处理的迭代顺序只是迭代桶数组并产生非空桶中的条目
  • 项目存储在桶[hash(key) % len(buckets)],因此哈希算法和桶数组大小的含义
  • 如果两个项目具有相同的模块化哈希,冲突将得到解决,对于开放寻址有很多算法(所有这些最终都会将其中一个项目放入“错误”的桶中,但它们的方式他们移动哪一个可能完全不同)
  • 不同的冲突解决算法对存储桶阵列已满有不同的敏感性,一些会在 70% 时开始降级,而另一些会在 90% 时正常,这会通知负载因子限制,因此当您调整大小时,新的物品的位置(和顺序)
  • 此外,不同的哈希表可以有不同的增长因子,即使所有其他因子相同,不同的增长因子也会导致桶数组的大小不同,因此一旦取模,分布(和顺序)就会不同已应用

从 Python 3.6 开始,P3 已经改变了 hashmaps 的实现以使用“自然排序”1,获得非插入顺序的唯一方法是交互 @987654326哈希映射的@字段然后显式取消引用相应的dk_entries 项目,这两个字段都不是 API 的一部分(在 C 级别更不用说 Python),即使它们是你也会必须检查the implementation details 以查看新的哈希图是否使用相同的哈希和增长阈值。这又无关紧要,因为无法访问内部详细信息。

[1]:你可以看pypy's explanation of the scheme作为入门,虽然CPython的实现不同/

【讨论】:

  • @jtlz2 不,它的实现是......实现依赖的。在 CPython(您可能正在使用的“默认”Python)中,它是在 C 中实现的。
  • 换句话说:它不是随机排序的,而是任意排序的。给定相同的“历史”(插入、删除等),并且在 3.3 之后,相同的hash seed,您会得到相同的订单。但即使在 3.7 之后,字典也不是 语义上的有序数据结构 - 这就是为什么 OrderedDict 仍然存在的原因。
  • @jtlz2 当然有可能,但工作量很大。不,它不是在 Python 中使用的,旧的实现是 4000 lines of C
  • @jtlz2:Python 2 语言的定义没有指定添加到字典中的值的顺序,而且它只是一个实现细节。在 Python 3.7 中,插入顺序的保留被添加到语言定义中(无论实现如何)。因为前者,你想做的事情无法完成——你不应该编写 Py2 代码,一开始就指望它是某种方式。
  • @martineau 不知不觉地依赖于迭代排序在非平凡的软件中相当容易,在将旧代码库从 Python 2 迁移到 Python 3 时我不得不处理很多(Python 3 的哈希随机化)。 Go 不会随机化每个 hashmap 迭代,只是因为他们觉得它很有趣。例如获取位的一种常见方法是简单地将存储在哈希图中的浮点数相加,因为它是浮点数,操作顺序会影响结果。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-14
  • 2014-03-10
  • 2017-11-15
相关资源
最近更新 更多