为什么python不包含有序的dict(默认情况下)?

时间:2012-11-13 03:35:31

标签: python

Python有一些很好的结构来建模数据。 以下是一些:

              +-------------------+-----------------------------------+
              | indexed by int    | no-indexed by int                 |
+-------------+-------------------+-----------------------------------+
| no-indexed  | [1, 2, 3]         | {1, 2, 3}                         |
| by key      | or                | or                                |
|             | [x+1 in range(3)] | {x+1 in range(3)}                 |
+-------------+-------------------+-----------------------------------+
| indexed     |                   | {'a': 97, 'c': 99, 'b': 98}       |
| by key      |                   | or                                |
|             |                   | {chr(x):x for x in range(97,100)} |
+-------------+-------------------+-----------------------------------+

为什么python默认不包含由key + int索引的结构(如PHP数组)?我知道有一个模拟这个对象的库(http://docs.python.org/3/library/collections.html#ordereddict-objects)。但这里是从文档中获取的“orderedDict”的表示:

OrderedDict([('pear', 1), ('apple', 4), ('orange', 2), ('banana', 3)])

拥有一个逻辑上应该像这样编写的本机类型不是更好:

['a': 97, 'b': 98, 'c': 99]

与orderedDict理解相同的逻辑:

[chr(x):x for x in range(97,100)]

在python设计中填充像这样的表格单元格是否有意义? 这有什么特别的原因尚未实施吗?

4 个答案:

答案 0 :(得分:17)

Python的字典作为哈希表实现。这些本质上是无序的数据结构。虽然可以添加额外的逻辑来跟踪顺序(如在Python 2.7和3.1+中的collections.OrderedDict中所做的那样),但是涉及到一个非平凡的开销。

例如,the recipe that the collections documentation suggest for use in Python 2.4-2.6需要的工作量是完成许多基本字典操作(例如添加和删除值)的两倍多。这是因为它必须维护一个双向链表以用于有序迭代,并且它需要一个额外的字典来帮助维护列表。虽然它的操作仍然是O(1),但常数项更大。

由于Python在任何地方使用dict实例(例如,对于所有变量查找),因此它们需要非常快或每个程序的每个部分都会受到影响。由于不经常需要有序迭代,因此在一般情况下避免它所需的开销是有意义的。如果您需要有序字典,请使用标准库中的字典(或者它建议的配方,如果您使用的是早期版本的Python)。

答案 1 :(得分:4)

您的问题似乎是“为什么Python没有带有序键的本机PHP样式数组?”

Python有三种核心非标量数据类型:list,dict和tuple。 Dicts和元组对于实现语言本身是绝对必要的:它们用于赋值,参数解包,属性查找等。虽然没有真正用于核心语言语义,但列表对于Python中的数据和程序非常重要。这三个必须非常轻量级,具有非常好理解的语义,并且尽可能快。

PHP风格的数组都不是这些。它们不是快速或轻量级的,运行时复杂性很差,而且它们混淆了语义,因为它们可以用于许多不同的事情 - 请看array functions。对于几乎所有用例,它们实际上都是可怕的数据类型,除了创建它们的非常窄的用例:表示x-www-form-encoded数据。即使对于这个用例,失败的是早期的密钥会覆盖以后密钥的值:在PHP ?a=1&a=2中会导致array('a'=>2)。 (在Python中处理此问题的一个常见结构是MultiDict,它已经订购了键值,并且每个键都可以有多个值。)

PHP有一种数据类型,几乎每个用例都必须使用它,而不是对任何用例都很好。 Python有许多不同的数据类型(一些核心,在外部库中更多),它们在更窄的用例中表现出色。

答案 2 :(得分:1)

添加包含更新信息的新答案:从 C Python3.6开始, dicts保留订单。虽然仍然无法索引。很可能因为基于整数的项查找是不明确的,因为dict键可以是int。 (存在一些自定义用例。)

不幸的是,documentation for dict尚未更新以反映(尚未),仍然说“键和值以非随机的任意顺序迭代”。具有讽刺意味的是,collections.OrderedDict文档提到了新行为:

  

在版本3.6中更改:接受PEP 468后,将保留传递给OrderedDict构造函数及其update()方法的关键字参数的顺序。< / p>

这是一篇提到some more details about it的文章:

  

一个次要但有用的内部改进:Python 3.6保留了更多结构的元素顺序。传递给函数的关键字参数,类中的属性定义和字典都保留了元素定义时的顺序。

因此,如果您只是为Py36编写代码,除非您使用popitemmove_to_end或基于订单的相等,否则不应该collections.OrderedDict

示例,在Python 2.7中:

>>> d = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 0: None}
>>> d
{'a': 1, 0: None, 'c': 3, 'b': 2, 'd': 4}

在Python 3.6中:

>>> d = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 0: None}
>>> d
{'a': 1, 'b': 2, 'c': 3, 'd': 4, 0: None}
>>> d['new'] = 'really?'
>>> d[None]= None
>>> d
{'a': 1, 'b': 2, 'c': 3, 'd': 4, 0: None, 'new': 'really?', None: None}
>>> d['a'] = 'aaa'
>>> d
{'a': 'aaa', 'b': 2, 'c': 3, 'd': 4, 0: None, 'new': 'really?', None: None}
>>> 
>>> # equality is not order-based
>>> d1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 0: None}
... d2 = {'b': 2, 'a': 1, 'd': 4, 'c': 3, 0: None}
>>> d2
{'b': 2, 'a': 1, 'd': 4, 'c': 3, 0: None}
>>> d1 == d2
True

答案 3 :(得分:0)

从python 3.7开始,这现在是字典的默认行为,这是3.6的实现细节,截至2018年6月已采用:')

  

dict对象的插入顺序保留性质已声明是Python语言规范的正式组成部分。

https://docs.python.org/3/whatsnew/3.7.html