【问题标题】:Why are python dictionaries not reversible for python3.7?为什么python3.7的python字典不可逆?
【发布时间】:2020-02-13 05:18:15
【问题描述】:

从 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 的典型安装已经是这种情况,如this post 中所述。


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

【问题讨论】:

    标签: python-3.x dictionary


    【解决方案1】:

    来自docs

    反转(seq)

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

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

    至于为什么,这已经被建议和讨论了here

    编辑:

    这些引用来自Python-Dev mailing list(线程“为dict添加__reversed__方法”,开始于25.05.18),我将从“概念”参数开始,第一个来自Antoine Pitrou:

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

    1. dict 和现在的 OrderedDict 差不多,应该支持 reversed() 也是;

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

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

    接着是 Raymond Hettinger 的回复:

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

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

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

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

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

    返回邮件列表,这是最后两条消息(均发布于 08.06.2018)。首先来自 Michael Selik:

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

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

    以 Guido van Rossum 的信息结束:

    这听起来对我来说是正确的。这样我们就会有两个版本:

    • 3.6,其中订单保留是在 CPython 中实现的,但在语言规范中

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

    如其他答案和 cmets 所述,自 3.8 版(14.10.2018)起,dicts 和 dictviews 都支持 reversed()

    【讨论】:

    • 你的第二个引用似乎是从那个线程中选择的。共识似乎是该功能将在 3.8 中添加。同样在 python 3.7 之前,普通的dict 对象根本没有排序(至少语言不能保证)所以reversed 也没有任何意义
    • 我没有狗在打架,我只是引用了他的第一反应。但你是对的,点得好 - 我删除了引用。
    • 谢谢。 python-dev 的讨论线程很有启发性。事实上,该功能已经在 python 3.8 中实现了,它刚刚在两天前(2019 年 10 月 14 日)发布。
    • 文档引用没有帮助,它只会引出下一个问题“那么,为什么 dict 类型不实现__reversed__”? python-dev链接有有用的内容,但相关部分应该直接在答案中复制(因为这样的场外链接容易腐烂)。
    【解决方案2】:

    Since python 3.8 reverse() is useable on dicts

    现在可以使用 reversed() 以反向插入顺序迭代字典和字典视图。 ( 由 Rémi Lapeyre 在 bpo-33462 中贡献。)

    【讨论】:

      【解决方案3】:

      python 3.8 更新

      现在可以使用 reversed() 以反向插入顺序迭代字典和字典视图

      >>> dict = {1: "1", 2: "2", 3: "3"}
      >>> reversed(dict)
      <dict_reversekeyiterator object at 0x7f72ca795130>
      

      【讨论】:

      • 此答案是否提供了其他答案、cmets 或问题本身尚未涵盖的任何新内容?
      • @normanius 它有一个 lil 可视化代码示例,可能对快速浏览器有帮助
      • py3.8 TypeError: 'dict' object is not reversible
      猜你喜欢
      • 2010-11-06
      • 2010-11-27
      • 1970-01-01
      • 2013-03-04
      • 2014-08-04
      • 2021-04-12
      • 2018-12-06
      • 2012-04-22
      • 2016-10-18
      相关资源
      最近更新 更多