如何在 Python 中按键对字典进行排序

Posted

技术标签:

【中文标题】如何在 Python 中按键对字典进行排序【英文标题】:How to sort dictionaries by keys in Python 【发布时间】:2011-06-06 06:19:23 【问题描述】:

谁能告诉我如何排序:

'a': [1, 2, 3], 'c': ['one', 'two'], 'b': ['blah', 'bhasdf', 'asdf'], 'd': ['asdf', 'wer', 'asdf', 'zxcv']

进入

'a': [1, 2, 3], 'b': ['blah', 'bhasdf', 'asdf'], 'c': ['one', 'two'],'d': ['asdf', 'wer', 'asdf', 'zxcv']

? 谢谢!

更新 1,代码示例:

所以我在做语言学。一篇文章被分解为存储在数据库中的单词,并具有各种属性,包括 para ID 和 sentence ID。任务:尝试重建原始文本。

从 DB 中获取 500 个连续单词

words = Words.objects.all()[wordId:wordId+500]
# I first create paragraphs, through which I can loop later in my django template,
# and in each para will be a list of words (also dictionaries). 
# So i am trying to get a dictionary with values that are lists of dictionaries. 
# 'pp' i make just for shorthanding a long-named variable.
paras=
para_high = para_low =  words[0].belongs_to_paragraph
for w in words:
    last_word = w
    pp = w.belongs_to_paragraph
    if pp >para_high:
        para_high = pp
    if pp < para_low:
        para_low = pp
    if pp in paras:
        paras[pp].append(w)
    else:
        list = [w]
        paras[pp] = list
# Since there are blank lines between paragraphs, in rebuilding the text as it 
    #  looked originally, I need to insert blank lines. 
    # Since i have the ID's of the paragraphs and they go somewhat like that: 1,3,4,8,9 
    #(the gaps between 1 & 3 and 4 & 8 i have to fill in with something else, 
    # which is why i had para_low and para_high to loop the range. 
isbr = True
for i in range(para_low, para_high+1):
    if i in paras:
        isbr = True
    else:
        if isbr:
            paras[i]=['break']
            isbr = False
        else:
            paras[i]=[]

然而,在这一点上,如果我尝试循环 dict 并重建文本,一些后面的 id'd 段落会出现在前面的段落之前,这就是不行。

更新2,循环代码:

        % for k,v in wording.iteritems()  %
        % if v[0] == 'break' %
        <br/>
        % else %
        </div><div class="p">% for word in v %% if word.special==0% % endif %<span class="word % if word.special == 0%clickable% endif%" wid="word.id" special="word.special" somethingElse=word.somethingElse> word.word </span>% endfor %
        % endif %
    % endfor %

【问题讨论】:

...为什么,确切地说,你想要这个吗? 我猜现在的代码说明了我为什么要这样做 "如果我尝试循环 dict 并尝试重建文本,一些后面的 id-d 段落会出现在前面的段落之前,并且不会这样做。"是的。所以使用sorted()。真的,就这么简单。 我认为:paras=sorted(paras) 在我看来,但发生的情况是我丢失了单词字典的数据结构。 word['type']='verb', word['special']='true' 我得到错误:“'list' object has no attribute 'keys'” 不,你没有得到一个“松散的结构”,你从字典中得到一个排序的键/值元组列表,然后你可以循环。你应该合理地(根据我的例子)在循环中使用sorted()for k,v in sorted(paras): 或类似的。此外,您完全跳过了代码的相关部分,即循环。 【参考方案1】:

字典没有顺序。

你可以调用 sorted 但这只是给你一个排序的键列表:

>>> sorted(d)
['a', 'b', 'c', 'd']

您可以将其视为一个可迭代对象并对键值元组进行排序,但是您只是得到了一个元组列表。这和 dict 不一样。

>>> sorted(d.items())
[
 ('a', [1, 2, 3]),
 ('b', ['blah', 'bhasdf', 'asdf']),
 ('c', ['one', 'two']),
 ('d', ['asdf', 'wer', 'asdf', 'zxcv'])
]

如果您使用的是 Python 2.7 或更新版本,您也可以考虑使用OrderedDict

添加了记住订单条目的dict子类

例如:

>>> d = collections.OrderedDict(sorted(d.items()))
>>> for k, v in d.items():
>>>     print k, v
一个 [1, 2, 3] b ['废话','bhasdf','asdf'] c ['一个','两个'] d ['asdf', 'wer', 'asdf', 'zxcv']

【讨论】:

好吧,但这对我帮助不大,是吗?也许没有内置函数,但有更复杂的用户编写的解决方案? 答案的原始版本中没有提到 OrderedDict。 @mgPePe 对我来说似乎是一个合适的答案,对你来说也是一个合理的解决方案 - 如果不是,你能提供更多关于你想要做什么的细节吗? 已排序!=已排序。如果他希望项目排序,他应该使用 sorted() 函数对键进行排序。如果他想让它们有序,他应该使用 OrderedDict。 @Matt - 我正在尝试获取已排序的字典。 OrderedDict 听起来是正确的,但它后来被添加到较新版本的 python 中,并且在 2.6 中不起作用。我收到“ImportError:无法导入名称 OrderedDict”。我想要得到的是另一个有序字典,而不是项目列表。只是为了确保我明白了:问题是python中的常规字典根本没有排序概念,而不是它没有预建的函数来排序,这就是你所说的“字典不”没有订单。您无法对它们进行排序。”?【参考方案2】:

正确的答案是,如果您希望字典中的项目按排序顺序排列,则应该在遍历字典时使用 sorted() 函数

for k, v in sorted(d.items()):
    print k, ':', v

for k in sorted(d):
   print d[k]

或类似的。

提到的 OrderedDict 适用于有顺序的字典。并且顺序与排序不同。你可以创建一个排序的 OrderedDict,是的,但是一旦你添加了一个新的键,它就不再排序了。 因此,无论如何您都需要使用 sorted() 在每次使用之前或每次操作之后对其进行排序。因此,OrderedDict 仅比普通字典更慢且更占用内存,而无需添加任何内容。

OrderedDict 不是用于已排序的字典,但对于其中项目具有某种排序的字典排序。例如,如果您想按照添加的顺序显示内容,或者您​​希望用户能够任意排序。

更新:进一步说明

为什么 OrderedDict 不是解决方案?因为 OrderedDict 是 ordered 而不是 sorted

考虑一个标准字典:

>>> d = 'a': 0, 'b': 1, 'c': 2, 'd': 3, 'e': 4, 'f': 5

它没有排序,正如我们在下面看到的,'c' 将排在 'b' 之前。它也没有顺序,如果我们添加新东西,它看起来像是随机顺序:

>>> d['g'] = 6
>>> d['i'] = 8
>>> d
'a': 0, 'c': 2, 'b': 1, 'e': 4, 'd': 3, 'g': 6, 'f': 5, 'i': 8

好的,那么让我们使用 OrderedDict:

>>> o = OrderedDict(sorted('a': 0, 'b': 1, 'c': 2, 'd': 3, 'e': 4, 'f': 5.items()))
>>> o
OrderedDict([('a', 0), ('b', 1), ('c', 2), ('d', 3), ('e', 4), ('f', 5)])

啊哈!排序!所以 OrderedDict 有效!?没有。

>>> o['i'] = 8
>>> o['g'] = 6
>>> o
OrderedDict([('a', 0), ('b', 1), ('c', 2), ('d', 3), ('e', 4), ('f', 5), ('i', 8), ('g', 6)])

什么? g 结束于 i?!?为什么!?因为 OrderedDict 没有排序,所以它是有序的。它会记住您添加东西的顺序。不是排序。这意味着每次使用它时都需要先对其进行排序。只要您不向其添加键,OrderedDict 只会保持排序。但是如果你不打算修改它,那么你就不需要字典。你也可以有一个清单。这是你从 sorted() 得到的:

>>> sorted(o.items())
[('a', 0), ('b', 1), ('c', 2), ('d', 3), ('e', 4), ('f', 5), ('g', 6), ('i', 8)]

但这与标准字典一样有效,因此 OrderedDictionary 没有帮助:

>>> sorted(d.items())
[('a', 0), ('b', 1), ('c', 2), ('d', 3), ('e', 4), ('f', 5), ('g', 6), ('i', 8)]

结论 所以每次你想以排序的方式遍历字典时,你需要这样做:

>>> for k in sorted(o):
...   print k, o[k]
... 
a 0
b 1
c 2
d 3
e 4
f 5
g 6
i 8

那是无论你使用什么字典。 OrderedDict 并不能真正帮助您,因为它不关心 排序,只关心您添加内容的 order

【讨论】:

+ 1 既然你正确解释了这个问题,我会马上删除我的(错误的)答案(或者为了完整起见,我明天会删除它);-) 嗯,但是当您尝试构建新的 Dict 时会出现问题。让我们来看看你的第一个循环。它确实可以正确打印以下内容:a:1、b:2、c:3 等等。但是,当您尝试用 new_dict[k]=v 替换打印行,然后在解释器中打印 v 时,您会得到另一个未排序的字典 a:1, c:3, b:2 @mgPePe:“新字典”? “其他”?字典排序。遍历它们时需要使用 sorted() 。总是。 @lennart:我是一个新手,我很难实现这一点。原因如下:我有一篇文章。它分为段落,句子,单词。我正在分析语言学。每个单词都有它的 para ID、sentence ID 和一堆属性。我想通过将单词连接在一起来重建原始文本。我提取了 500 个按 ID 排序的单词并将它们存储在一个字典中,但是在我做了几次操作之后,这些单词不再排序,我试图重建的文本是一团糟。 @mgPepe:是的,因此您必须在字典准备好重建时对其进行排序。你使用sorted() 来做到这一点。重建时循环遍历字典,对吗?只需在该循环中围绕它们粘贴一个 sorted() 即可。如果这不起作用,请发布代码的相关位。 除非您实际上不想对其进行排序,否则您希望对其进行排序,即取出它们时的顺序与放入时的顺序相同。然后您可以从一开始就使用 OrderedDict 而不是一个标准的字典。【参考方案3】:

值得注意的是,Python 有许多字典实现,它们按排序顺序维护键。考虑 sortedcontainers 模块,它是纯 Python 和 fast-as-C 实现。有一个 performance comparison 与其他快速且功能完整的实现相互进行了基准测试。

例如:

>>> from sortedcontainers import SortedDict
>>> d = 'a': [1, 2, 3], 'c': ['one', 'two'], 'b': ['blah', 'bhasdf', 'asdf'], 'd': ['asdf', 'wer', 'asdf', 'zxcv']
>>> s = SortedDict(**d)
>>> s.keys()
SortedSet(['a', 'b', 'c', 'd'])

您也可以用 SortedDict 完全替换您对 dict 的使用,因为它支持快速获取/设置操作和按键的排序迭代。

【讨论】:

【参考方案4】:

这是一个快速简便的功能,可用于按键对字典进行排序。

将此代码放在名为sdict.py 的单独文件中:

def sortdict(dct):
    kys = dct.keys()
    kys.sort()
    from collections import OrderedDict
    d = OrderedDict()
    for x in kys: 
        for k, v in dct.iteritems():
            if (k == x):
                d[k] = v
    return d

现在,将此代码放入名为 test.py 的单独文件中,以使用示例字典对其进行测试:

from sdict import sortdict
import json
dct = 'sizes':[32,28,42], 'dog':'schnauser', 'cat':'siamese', 'bird':'falcon'
dctx = sortdict(dct)
print json.dumps(dctx) 

最后,从命令行调用test.py

$ python test.py
"bird": "falcon", "cat": "siamese", "dog": "schnauser", "sizes": [32, 28, 42]

我只是使用 json.dumps 行来向您展示它是一个实际的字典,而不仅仅是一个字符串表示。你也可以使用 type() 函数来测试它。

我在示例字典中包含了一个带有数值的嵌套列表,以表明该函数可以处理更复杂的字典,而不仅仅是基于字符串的单层字典。

代码非常简单,因此可以很容易地修改它以按值排序,如果这是您的偏好 - 尽管如果某些值是对象,如列表、元组或其他字典,按值排序将没有意义.

诚然,这只适用于 python 2.7 或更高版本。

干杯, -=卡梅隆

【讨论】:

【参考方案5】:

正如另一个答案提到的,字典键的顺序是任意的,你不应该依赖它。

如果您使用的是 Python 2.7 或 3.1 或更高版本,请试用collections.OrderedDict(2.7 docs;3.1 docs;另请参阅PEP 372)。文档中有一个指向 pure-Python version of OrderedDict 的链接,该链接适用于早期的 Python 版本。

【讨论】:

【参考方案6】:

还值得一提的是 heapq 中的 nlargest 例程。这将排序并返回前 N 个项目。根据实际需要,如果您使用 key 参数,这可能会很方便。我主要提到这一点,因为我几天前发现了它,它完全符合我的要求。见PEP 0265 和Heapq。

【讨论】:

【参考方案7】:

我会在其他人已经解释过的内容上加一分钱。在一个特定情况下,我碰巧遇到了完全相同的问题。为了编写稳定的单元测试,我需要字典的输出始终相同。

如果碰巧这是您想要实现的目标,或者其他与输出相关的任务,您根本不需要对任何内容进行排序,只需使用 pprint 模块,它会按键对字典进行排序。

>>> d = 'a':1, 'b':2, 'c':3
>>> print d
'a': 1, 'c': 3, 'b': 2

>>> from pprint import pprint
>>> pprint(d)
'a': 1, 'b': 2, 'c': 3

【讨论】:

thanx @kriss,我在 SO 上阅读了另一个关于它的问题,但 print 本身并不是我需要的,而且我认为它不会在所有版本的 python 中排序

以上是关于如何在 Python 中按键对字典进行排序的主要内容,如果未能解决你的问题,请参考以下文章

python怎么对字典进行排序

如何对字典按键值大小进行排序

Python对字典分别按键(key)和值(value)进行排序

如何使用 Pandas 在 Python 中对字典中的数据进行排序

如何在 Python 中对存储在字典中的 IP 地址进行排序?

如何在 Python 中对大量字典进行排序而不加载到内存中