【问题标题】:OrderedDict Isn't Ordered?OrderedDict 没有排序?
【发布时间】:2017-01-26 04:31:10
【问题描述】:

我正在尝试使用OrderedDict,但它一直在乱序创建。例如,

from collections import OrderedDict
OrderedDict(a=1,b=2,c=3)

产量

OrderedDict([('a', 1), ('c', 3), ('b', 2)])

而不是预期

OrderedDict([('a', 1), ('b', 2), ('c', 3)])

如何确保它按照我想要的正确顺序创建?

【问题讨论】:

标签: python python-2.7


【解决方案1】:

collections.OrderedDict 跟踪添加元素的顺序。这将在循环中正常工作:

c = collections.OrderedDict()
for a,b in zip('abc', (1,2,3)):
    c[a] = b

但是,表达式OrderedDict(a=1,b=2,c=3) 通过将几个关键字参数传递给其构造函数来创建OrderedDict。在 Python 2.7 中,不保证关键字参数的顺序。如果你想这样做,你必须迁移到 Python 3.6,它实现了 PEP 468,Preserving the order of **kwargs in a function

函数定义中的**kwargs 语法表明解释器应该收集所有与其他命名参数不对应的关键字参数。但是,Python 不会保留将这些收集的关键字参数传递给函数的顺序。在某些情况下,顺序很重要。此 PEP 规定收集的关键字参数在函数体中作为有序映射公开。

【讨论】:

  • 嗯。没有意识到他们实际上决定记录这种行为并保证它。很高兴知道。
  • @ShadowRanger - some ambiguity 关于 whether 基本 dict 类型被排序为除了 CPython 实现细节之外的任何内容,但是,是的,关键字参数从 3.6 开始正式成为语言的一部分.
  • @Copperfield - 是的,它向构造函数发送一个参数,然后以类似于演示循环的方式迭代该参数。该参数永远不会发送到任意顺序的容器,它是一个序列,因此它的顺序是有保证的。
【解决方案2】:

奇怪的是它还没有被提及,但OrderedDict 的表示向您展示了如何创建它以保持顺序:

OrderedDict([('a', 1), ('b', 2), ('c', 3)])

这样的表示并不意味着障碍 - 这是因为该表示可用于创建相同的有序OrderedDict


为了完整性(已经提到过),顺序丢失了,因为OrderedDict(a=1, b=2, c=3) 将这些参数捕获为**kwargs,这是一个正常的无序-dict。至少在 python 3.6 出现并制作 promise 之前,kwargs 的顺序将在您传递时保持不变:

Python 3.6 的新特性

PEP 468:保留关键字参数顺序

现在保证函数签名中的**kwargs 是保留插入顺序的映射。

【讨论】:

  • 可能值得一提的是,the following item in that docs link 表示 Python 3.6 中的普通 dict 现在维护插入顺序,但目前这应该“被视为实现细节,不应依赖(这可能未来会发生变化,但希望在更改语言规范以强制所有当前和未来的 Python 实现保持顺序的语义之前,在几个版本中在语言中使用这个新的 dict 实现”
  • @PM2Ring 实际上 3.6 中的 Preserving Keyword Argument Order 不是实现细节。所有 other dicts 都在 3.6 中排序是一个实现细节。 :-) 但最好在帖子下方发表评论,这是重要信息。谢谢。
  • @同意!在我之前的评论中,我并不是在暗示 Preserving Keyword Argument Order 是一个实现细节。但是,当然,让普通 dict 维护插入顺序肯定会使保留关键字 arg 顺序变得容易得多。 :)
【解决方案3】:

阅读the docs

OrderedDict 构造函数和 update() 方法都接受关键字参数,但它们的顺序丢失了,因为 Python 的函数调用语义使用常规无序字典传入关键字参数。

必须将输入作为元组序列(或现有的有序字典类型)传递以保持顺序。

请注意,Python 3.6 现在是 provides a guarantee that keyword arguments are passed in the same order they appear in the code (from left to right),感谢 PEP 468,因此在 Python 3.6 及更高版本上,您的代码将正常工作。

【讨论】:

  • 在 Python 3.6 中保证排序
【解决方案4】:

这是因为您传递的关键字参数(variable = value, ) 将首先合并到Python 字典中。 Python 字典是无序的。 kwds 将是您在 Init 签名中看到的那个字典。

Init signature: OrderedDict(self, *args, **kwds)

当您传递关键字参数时,这就是 OrderedDict 内部初始化的方式:

for key, value in kwds.items():
   self[key] = value

由于kwds 是无序的,你会得到一个无序的OrderedDict。

你可以像这样创建有序的字典:

from collections import OrderedDict
from string import ascii_lowercase

d = OrderedDict()
for a,b in enumerate(ascii_lowercase[:3], 1):
    d[b] = a

或者:

n=3
d = OrderedDict(zip(ascii_lowercase[:n], range(1,n+1))) 
print d 

输出:

OrderedDict([('a', 1), ('b', 2), ('c', 3)])

【讨论】:

  • 好的,但是如何创建所需的OrderedDict 呢?
  • @wogsland 应该使用 ascii_lowercase 吗? :)
【解决方案5】:

您可以使用sorted() 创建所需的映射:

dict = {"a":"some_value", "b":"other_value", "c":"foo"}
ordered = OrderedDict(sorted(dict.items(), key=lambda t: t[0]))

这会在将项目传递给OrderedDict 构造函数之前对其进行排序。

key= 部分设置排序,t[0] 按字典键排序。

【讨论】:

  • key= 是不必要的 - 只需按元组 (key, value) 进行排序,无论如何都会生成一个按键排序的字典。
猜你喜欢
  • 2020-02-07
  • 1970-01-01
  • 2013-07-22
  • 2011-12-23
  • 2021-12-07
  • 1970-01-01
  • 2011-10-04
  • 1970-01-01
  • 2020-03-26
相关资源
最近更新 更多