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

32

有人可以告诉我如何对这个进行排序:

{'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,代码示例:

因此,我正在进行语言学研究。一篇文章被分解为单词并存储在数据库中,其中包括段落ID和句子ID等所有属性。任务是:尝试重建原始文本。

从数据库获取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]=[]

然而,在这一点上,如果我尝试循环字典并重建文本,一些较晚的带有id的段落会出现在先前的段落之前,这就不对了。

更新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 %}

4
为什么你想要这个,确切地说,你为什么要这个? - Karl Knechtel
1
我想现在的代码说明了我为什么需要那个。 - mgPePe
我有以下代码:paras=sorted(paras)。但实际上,我的数据结构中的单词字典丢失了其结构。例如,word['type']='verb',word['special']='true'。我收到错误消息:“'list' object has no attribute 'keys'”。 - mgPePe
啊,所以.belongs_to_paragraph不是内置类型,而是其他东西。它是什么类型的对象?你能从中得到可排序的东西吗?比如pp = w.belongs_to_paragraph.value或者其他什么? - Lennart Regebro
但是 sorted 函数总是将字典转换为列表,因此它最终会再次传递一个列表。模板可以循环字典,但问题是我无法以 Python 的方式准备正确的东西。无论如何,我很钦佩你的帮助和耐心。 - mgPePe
显示剩余9条评论
7个回答

62

字典没有顺序。

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

>>> sorted(d)
['a', 'b', 'c', 'd']
你可以将其视为可迭代对象并对键值元组进行排序,但这样你只是得到了一个元组列表。那不同于一个字典。
>>> sorted(d.items())
[
 ('a', [1, 2, 3]),
 ('b', ['blah', 'bhasdf', 'asdf']),
 ('c', ['one', 'two']),
 ('d', ['asdf', 'wer', 'asdf', 'zxcv'])
]
如果你使用的是Python 2.7或更新版本,你也可以考虑使用一个OrderedDict。它是一个记住添加顺序的字典子类。
例如:
>>> d = collections.OrderedDict(sorted(d.items()))
>>> for k, v in d.items():
>>>     print k, v
a [1, 2, 3]
b ['blah', 'bhasdf', 'asdf']
c ['one', 'two']
d ['asdf', 'wer', 'asdf', 'zxcv']

1
好的,但这对我并没有太大帮助,是吗?也许没有内置函数,但有一个更复杂的用户编写的解决方案? - mgPePe
2
答案的原始版本中没有提到OrderedDict。 - Nicholas Riley
3
@mgPePe 给我的回答看起来很合适,对你来说也是一个合理的解决方案 - 如果不是,请详细说明你想要做什么? - Matt Curtis
3
有序并不等于已排序。如果他想要将条目排序,则应使用sorted()函数对键进行排序。如果他想要它们有序,则应该使用OrderedDict。 - Lennart Regebro
@mgPepe:FYI:在我的答案中,你会找到一个链接指向 Python 2.4 --> Python 2.6 的官方文档中的规范实现。 - ChristopheD
显示剩余4条评论

37

正确的答案是,如果你想要按特定顺序获取字典中的项目,应该在循环字典时使用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是有序的而不是排序的

考虑一个标准字典:

>>> 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)]

但是标准字典同样可以很好地实现这个功能,因此有序字典并没有帮助:

>>> 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

而这无论你使用哪个字典都是适用的。有序字典并不能真正地帮助你,因为它不关心排序,只关心添加元素的顺序。


由于您正确解读了问题,我会立即删除我的(错误)回答(或者为了完整起见,我会明天删除它);-) - ChristopheD
嗯,但问题出现在尝试构建新字典时。让我们看看你的第一个循环。它确实正确地打印了类似于a:1,b:2,c:3等的内容。但是,当您尝试用new_dict [k] = v替换打印行,然后在解释器中打印v时,您会得到另一个未排序的字典,其中包含a:1,c:3,b:2。 - mgPePe
@mgPePe:“新字典”?“另一个”?字典不是排序的。当您循环遍历它们时,您需要使用sorted()。始终如此。 - Lennart Regebro
@lennart:我是个新手,实现这个有点难。原因是:我有一篇文章,它被分成段落、句子和单词。我正在进行语言学分析。每个单词都有它的段落ID、句子ID以及一堆属性。我想通过将单词连接在一起来重建原始文本。我按ID提取了500个单词并将它们存储在字典中,但在进行了几次操作后,这些单词不再排序,我试图重建的文本变得混乱了。 - mgPePe
@mgPepe:是的,所以当字典准备重建时,您必须对它们进行排序。您可以使用sorted()来完成这项工作。在重建时循环遍历字典,对它们进行排序即可。如果这样做不起作用,请发布您代码的相关部分。除非您实际上不想要排序,而是想要有序,即在取出它们时与放入它们时相同的顺序。那么您可以从一开始就使用OrderedDict而不是标准dict。 - Lennart Regebro
好的,让我把我的代码粘贴上去以说明问题的更新。 - mgPePe

5
值得注意的是,Python有许多字典实现可以维护排序后的键。考虑使用sortedcontainers模块,它是纯Python实现且速度快如C语言实现。这里有一份性能比较,其中包含其他快速且功能完备的实现相互进行基准测试。
例如:
>>> 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,因为它支持快速的get/set操作和按键排序的迭代。

2

以下是一个快速简便的函数,可以按照键值对对字典进行排序。

将以下代码放入名为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或更高版本。
祝好,
-= Cameron

1

正如其他答案所提到的,字典键的顺序是任意的,您不应该依赖它。

如果您正在使用Python 2.7或3.1或更高版本,请尝试使用collections.OrderedDict2.7文档; 3.1文档; 还请参阅PEP 372)。文档中有一个链接指向OrderedDict的纯Python版本,可在早期的Python版本上使用。


0
值得一提的是,heapq中的nlargest例程也许也很有用。它可以对前N个元素进行排序并返回。根据实际需求,如果您玩转key参数,这可能会很方便。我之所以提到这一点,是因为几个晚上前我发现了它,并且它正好满足了我的要求。请参见PEP 0265Heapq

0

我想补充一下别人已经解释过的内容。在某个具体的情况下,我碰巧遇到了完全相同的问题。我需要字典的输出始终保持不变,以便编写稳定的单元测试。

如果你也是为了实现这个目标或其他与输出相关的任务,你完全不需要对任何东西进行排序,只需使用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}

谢谢@kriss,我在SO上看到了另一个关于它的问题,但是仅仅print并不能满足我的需求,并且我认为它在所有版本的Python中都不能进行排序。 - mgPePe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接