OrderedDict 如何知道已经实例化的字典的元素顺序?
How can OrderedDict know about the element order of an already instantiated dict?
我在玩 Python 3.6 中的 OrderedDict
类型,对其行为感到惊讶。当我在 IPython:
中创建一个像这样的简单 dict
时
d = dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
我得到:
{'guido': 4127, 'jack': 4098, 'sape': 4139}
作为输出,出于某种原因,它在实例化时不保留元素的顺序。现在,当我像这样从 d
创建一个 OrderedDict
时:
od = OrderedDict(d)
输出为:
OrderedDict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
现在我问自己,OrderedDict
-构造函数如何知道 d
实例化时元素的顺序?它的行为是否始终相同,这样我就可以依赖 OrderedDict
?
中元素的顺序
我已经在阅读有关字典和 OrderedDict
的 Python 文档,但我没有找到问题的答案。
(sys.version
) 的输出:
In[22]: sys.version
Out[22]: '3.6.1 (default, Apr 4 2017, 09:40:21) \n[GCC 4.2.1 Compatible Apple LLVM 8.1.0 (clang-802.0.38)]'
很明显自定义挂钩 (sys.displayhook
) that IPython uses to display output is pretty printing things (using it's own pretty printer)。
通过直接调用displayhook
你可以看到它是如何破坏插入顺序的:
In [1]: from sys import displayhook
...: displayhook({'1': 0, '0': 1})
Out[1]: {'0': 1, '1': 0}
此外,如果您改为获取字典 str
(发送要显示的字符串而不是字典对象),您将获得正确的预期顺序:
In [2]: d = dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
...: d
Out[2]: {'guido': 4127, 'jack': 4098, 'sape': 4139}
In [3]: str(dict(t))
Out[3]: "{'sape': 4139, 'guido': 4127, 'jack': 4098}"
类似地 print
ing 它。
我不确定为什么 IPython 使用 3.6
这样做,这很令人困惑(编辑:参见相关 issue on GitHub)。在您的标准 Python REPL 中,此行为不会出现,因为 sys.displayhook
未实现任何漂亮的打印。
您创建的字典 d
确实 保持插入顺序,这就是 OrderedDict
保持相同顺序的原因。
当然,它确实是一个实现细节。在这种情况发生改变之前(而且看起来确实会发生),您应该坚持使用 OrderedDict
来可靠地维护跨实现的顺序。
顺便说一下,如果你想禁用它,你可以使用 --no-pprint
选项启动 IPython 来禁用它的漂亮打印机:
➜ ipython --no-banner --no-pprint
In [1]: dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
Out[1]: {'sape': 4139, 'guido': 4127, 'jack': 4098}
您可能知道,Python 中的词典不是根据语言规范排序的。它们确实有一个固有的顺序,但这个顺序是任意的。
因此,当您将标准字典传递给 OrderedDict
的构造函数时,新的 OrderedDict
将通过迭代其值从原始字典的值中填充。这样,将使用字典的固有顺序,这就是您将在最终 OrderedDict
.
中看到的内容
现在,在 Python 3.6 中,默认字典的实现发生了变化。正如在 上讨论和解释的那样,标准词典现在保留插入顺序。这就是为什么当您从 Python 3.6 字典创建 OrderedDict
时,原始顺序也会被保留。
这是否意味着 OrderedDict
在 Python 3.6+ 中已过时?不,因为标准词典的顺序保留是一个 实现细节 。与以前实现的任意顺序不同,新字典恰好具有“正确”的顺序。但这绝不是语言规范所保证的,其他实现可能是这样,也可能不是。因此,您不能也不应该依赖它。
顺便说一句。请注意,Python 3.6(语言,而不仅仅是实现)确实保证保留 OrderedDict
的关键字参数的顺序。例如。这保留了顺序:
>>> OrderedDict(sape=4139, guido=4127, jack=4098)
OrderedDict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
在 3.6 中,作为实现细节,所有 dict
都是有序的。你被IPython愚弄了:在3.6之前,键的顺序是任意的,所以为了用户友好,IPython对dict
和set
的交互输出(其中正常 Python 只会打印 repr
) 对键进行排序。这就是为什么您的 dict
看起来是按字母顺序排列的。当 运行 在 3.6+ 上时,IPython 可能最终会放弃该行为,因为正如您所注意到的,它非常令人困惑。
如果你显式 print
,而不是依赖 ipython
为你输出前面表达式的结果,你将绕过 ipython
的 REPL 魔法并看到"natural"订单。与 dict
进行交互的任何其他方式也是如此,因为迭代将按预期顺序进行。
我在玩 Python 3.6 中的 OrderedDict
类型,对其行为感到惊讶。当我在 IPython:
dict
时
d = dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
我得到:
{'guido': 4127, 'jack': 4098, 'sape': 4139}
作为输出,出于某种原因,它在实例化时不保留元素的顺序。现在,当我像这样从 d
创建一个 OrderedDict
时:
od = OrderedDict(d)
输出为:
OrderedDict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
现在我问自己,OrderedDict
-构造函数如何知道 d
实例化时元素的顺序?它的行为是否始终相同,这样我就可以依赖 OrderedDict
?
我已经在阅读有关字典和 OrderedDict
的 Python 文档,但我没有找到问题的答案。
(sys.version
) 的输出:
In[22]: sys.version
Out[22]: '3.6.1 (default, Apr 4 2017, 09:40:21) \n[GCC 4.2.1 Compatible Apple LLVM 8.1.0 (clang-802.0.38)]'
很明显自定义挂钩 (sys.displayhook
) that IPython uses to display output is pretty printing things (using it's own pretty printer)。
通过直接调用displayhook
你可以看到它是如何破坏插入顺序的:
In [1]: from sys import displayhook
...: displayhook({'1': 0, '0': 1})
Out[1]: {'0': 1, '1': 0}
此外,如果您改为获取字典 str
(发送要显示的字符串而不是字典对象),您将获得正确的预期顺序:
In [2]: d = dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
...: d
Out[2]: {'guido': 4127, 'jack': 4098, 'sape': 4139}
In [3]: str(dict(t))
Out[3]: "{'sape': 4139, 'guido': 4127, 'jack': 4098}"
类似地 print
ing 它。
我不确定为什么 IPython 使用 3.6
这样做,这很令人困惑(编辑:参见相关 issue on GitHub)。在您的标准 Python REPL 中,此行为不会出现,因为 sys.displayhook
未实现任何漂亮的打印。
您创建的字典 d
确实 保持插入顺序,这就是 OrderedDict
保持相同顺序的原因。
当然,它确实是一个实现细节。在这种情况发生改变之前(而且看起来确实会发生),您应该坚持使用 OrderedDict
来可靠地维护跨实现的顺序。
顺便说一下,如果你想禁用它,你可以使用 --no-pprint
选项启动 IPython 来禁用它的漂亮打印机:
➜ ipython --no-banner --no-pprint
In [1]: dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
Out[1]: {'sape': 4139, 'guido': 4127, 'jack': 4098}
您可能知道,Python 中的词典不是根据语言规范排序的。它们确实有一个固有的顺序,但这个顺序是任意的。
因此,当您将标准字典传递给 OrderedDict
的构造函数时,新的 OrderedDict
将通过迭代其值从原始字典的值中填充。这样,将使用字典的固有顺序,这就是您将在最终 OrderedDict
.
现在,在 Python 3.6 中,默认字典的实现发生了变化。正如在 OrderedDict
时,原始顺序也会被保留。
这是否意味着 OrderedDict
在 Python 3.6+ 中已过时?不,因为标准词典的顺序保留是一个 实现细节 。与以前实现的任意顺序不同,新字典恰好具有“正确”的顺序。但这绝不是语言规范所保证的,其他实现可能是这样,也可能不是。因此,您不能也不应该依赖它。
顺便说一句。请注意,Python 3.6(语言,而不仅仅是实现)确实保证保留 OrderedDict
的关键字参数的顺序。例如。这保留了顺序:
>>> OrderedDict(sape=4139, guido=4127, jack=4098)
OrderedDict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
在 3.6 中,作为实现细节,所有 dict
都是有序的。你被IPython愚弄了:在3.6之前,键的顺序是任意的,所以为了用户友好,IPython对dict
和set
的交互输出(其中正常 Python 只会打印 repr
) 对键进行排序。这就是为什么您的 dict
看起来是按字母顺序排列的。当 运行 在 3.6+ 上时,IPython 可能最终会放弃该行为,因为正如您所注意到的,它非常令人困惑。
如果你显式 print
,而不是依赖 ipython
为你输出前面表达式的结果,你将绕过 ipython
的 REPL 魔法并看到"natural"订单。与 dict
进行交互的任何其他方式也是如此,因为迭代将按预期顺序进行。