首页 > 解决方案 > 为什么python3.7的python字典不可逆?

问题描述

从 3.7 开始,标准 python 字典保证保持插入顺序。(*)

d = {'b': 1, 'a': 2}
for k in d: 
    print(k)
# Prints always 'b' before 'a'.

换句话说,字典键保持严格的顺序。原则上,这将允许密钥是可逆的。但是,以下都不起作用:

# TypeError: 'dict' object is not reversible
for k in reversed(d): 
    print(k)

# TypeError: 'dict_keys' object is not reversible
for k in reversed(d.keys()): 
    print(k)

问题:这种行为背后的原因是什么?为什么 dicts 不可逆?未来有没有改变这种行为的计划?

解决方法当然有效:

for k in reversed(list(d.keys())): 
    print(k)

(*) 事实上,正如本文所讨论的,python 3.6 的典型安装已经是这种情况


更新:从python 3.8开始,字典实际上是可逆的。接受的答案是指 Guido 和其他核心开发人员之间的讨论导致了这个决定。简而言之,他们将语言一致性与实施工作和用户的实际利益进行了权衡。

标签: python-3.xdictionary

解决方案


文档

反转( seq )

返回一个反向iterator。seq 必须是具有__reversed__()方法或支持序列协议的对象(__len__()方法和__getitem__()具有从 0 开始的整数参数的方法)。

一个dict对象没有实现__reversed__。它确实实现了后两种方法。但是,__getitem__将键作为参数,而不是整数(从 0 开始)。

至于为什么,这已经在这里提出和讨论过。

编辑:

这些引用来自Python-Dev 邮件列表(线程“Add __reversed__ methods for dict”,开始于 25. 05. 18),我将从“概念”参数开始,第一个来自 Antoine Pitrou:

OrderedDict 已经支持 reversed() 毫无价值。争论可能是双向的:

  1. dict 类似于现在的 OrderedDict,所以它也应该支持 reversed();

  2. 您可以使用 OrderedDict 明确表示您关心订购;无需向 dict 添加任何内容。

我的想法是,常规字典的保证插入顺序是全新的,因此这个概念需要一段时间才能适应并成为日常思考字典的一部分。一旦发生这种情况,可能不可避免地会出现用例,并且 __reversed__ 将在某个时候添加。实现看起来很简单,并且期望有限有序集合是可逆的并不是概念上的飞跃。

随后是 Raymond Hettinger 的回复:

鉴于字典现在跟踪插入顺序,想知道最近的插入似乎是合理的(即循环遍历任务字典中最近添加的任务)。其他可能的用例可能与我们使用 Unix tail 命令的方式相对应。

如果出现这些用例,最好 __reversed__ 已经得到支持,这样人们就不会试图使用 popitem() 调用然后重新插入来实现丑陋的解决方法。

邮件列表中表达的主要担忧是,至少在某些实现中,这会增加过多的膨胀或降低内存效率(必须有双链表而不是单链表),这是 Inada Naoki 对 Python 错误跟踪器的引用(问题 33462 ):

“有订单”并不意味着“可逆”。例如,单链表是有序的,但不可逆。

虽然 CPython 实现可以提供高效__reverse__的 ,但添加__reverse__意味着所有Python 实现都应该提供它。例如,某些 Python 实现可能能够使用 hashmap + 单链表实现 dict。如果__reverse__添加了,就不可能了。

返回邮件列表,这是最后两条消息(均发布于 08.06.2018)。首先来自迈克尔·塞利克:

我是否正确地说共识是 +1 以包含在 v3.8 中?

线程中的最后一点是 INADA Naoki 研究各种实现并决定在 3.8 中包含此功能是可以的。据我了解,Guido 同意 INADA 的建议,等待 MicroPython 实现 v3.7。既然 INADA 改变了主意,我猜这一切都赞成?

以 Guido van Rossum 的信息结尾:

这听起来对我来说是正确的。然后,我们将有两个版本,情况就是这样:

  • 3.6 在 CPython 中但在语言规范中实现了订单保留

  • 3.7 它也被添加到语言规范中

如其他答案和评论中所述,reversed()自 3.8 版(14.10.2018)起支持 dicts 和 dictviews。


推荐阅读