在Python中,如何按照键的顺序对字典进行迭代?

227

有一个已经存在的函数,它以以下形式结束,其中d是一个字典:

return d.iteritems()

我想要返回一个按照字典键排序的迭代器,而不是未排序的迭代器。该怎么做?

10个回答

184

虽然没有进行过非常广泛的测试,但在Python 2.5.2中可以正常工作。

>>> d = {"x":2, "h":15, "a":2222}
>>> it = iter(sorted(d.iteritems()))
>>> it.next()
('a', 2222)
>>> it.next()
('h', 15)
>>> it.next()
('x', 2)
>>>

如果您习惯于使用 for key, value in d.iteritems(): ... 而不是迭代器,那么上面的解决方案仍然适用。

>>> d = {"x":2, "h":15, "a":2222}
>>> for key, value in sorted(d.iteritems()):
>>>     print(key, value)
('a', 2222)
('h', 15)
('x', 2)
>>>

在Python 3.x中,使用d.items()代替d.iteritems()来返回一个迭代器。

29
使用.items()代替iteritems():正如@Claudiu所说,iteritems()在Python 3.x中不可用,但是items()从Python 2.6开始就可用。 - Remi
40
这并不显而易见。实际上,items() 创建了一个列表,因此使用了内存,而 iteritems() 基本上不使用内存。使用哪个主要取决于字典的大小。此外,自动的 Python 2 到 Python 3 转换工具 (2to3) 会自动处理从 iteritems()items() 的转换,所以不需要担心这个问题。 - Eric O. Lebigot
如果getitem和sorted迭代都非常频繁使用,有可能进行优化吗? - HoverHell
6
使用collections.OrderedDict,排序一次后始终按排序顺序获取项目。 - Mark Harviston
9
但是,即使iteritems()不使用内存,sorted()仍然需要将所有内容拉入内存,因此在内存方面,在这里使用items()iteritems()之间没有区别。 - Richard
8
虽然所有元素在使用 items() 时必须被存储两次(一次在 items() 返回的列表中,一次在排序后的列表中),但使用 iteritems() 只需要存储一次(仅在排序后的列表中)。 - Eric O. Lebigot

86

使用 sorted() 函数:

return sorted(dict.iteritems())

如果你想要一个实际的迭代器来遍历排序后的结果,由于sorted()返回一个列表,所以可以使用:

return iter(sorted(dict.iteritems()))

这对我来说失败了:<type 'exceptions.TypeError'>:iter()返回类型为'list'的非迭代器 - mike
这可能是因为您使用了“dict”作为变量名。 “dict”实际上是字典类型的名称。 在这里使用另一个名称,例如“mydict”,问题就解决了。 - utku_karatas
1
仍未生效。你确定 sorted() 返回的是另一个迭代器,而不是常规列表吗? - mike
这个异常是在什么时候和哪里发生的?你可以毫无问题地遍历一个列表。 - user3850
1
同意,跳过。我认为除了在文件中跳过行之外,我从未直接调用.next()。我们的iter(sorted(dict.iteritems()))解决方案最终会在“sorted(”阶段在内存中复制整个dict,因此主要迭代器优势似乎已经丧失 :) - user44484
显示剩余3条评论

43

字典的键是存储在哈希表中的,所以它们的“自然顺序”是伪随机的。任何其他排序都是字典使用者的概念。

sorted() 总是返回一个列表,而不是一个字典。如果你传递给它一个 dict.items()(它会产生一个元组列表),它将返回一个元组列表 [(k1,v1), (k2,v2), ...],可以像字典一样在循环中使用,但它 绝对不是一个字典

foo = {
    'a':    1,
    'b':    2,
    'c':    3,
    }

print foo
>>> {'a': 1, 'c': 3, 'b': 2}

print foo.items()
>>> [('a', 1), ('c', 3), ('b', 2)]

print sorted(foo.items())
>>> [('a', 1), ('b', 2), ('c', 3)]

以下内容看起来像是循环中的字典,但实际上它是一个元组列表被拆分成键、值对。
for k,v in sorted(foo.items()):
    print k, v

大致相当于:

for k in sorted(foo.keys()):
    print k, foo[k]

2
使用 sorted(foo) 替代 sorted(foo.keys()) 会更好,因为在迭代字典时会返回其键(如果 sorted() 对可迭代对象的处理方式不需要创建中间列表 foo.keys() 的话,就具有这种优势)。 - Eric O. Lebigot
不知道哪种更快、更省内存,是k in sorted(foo.keys())提取键,还是for k,v in sorted(foo.items())返回字典的列表副本对。我猜想应该是sorted(foo.keys()) - CrandellWS
1
@CrandellWS:回答时间问题的最佳方式是使用Python timeit 模块。 - Peter Rowell
@peter Rowell 谢谢,我甚至不知道 timeit,我会试一下。 - CrandellWS
1
@frank -- 简短回答:不行。字典是一个数组,实际的键是提供的键值的哈希值。虽然有些实现可能相当可预测,甚至有些可能会遵守这个约定,但是当涉及到哈希排序时,我什么也不指望。请参见此帖子了解更多关于3.6+行为的信息。特别注意第一个答案。 - Peter Rowell
显示剩余4条评论

35

Greg的回答是正确的。请注意,在Python 3.0中,您将不得不执行

sorted(dict.items())

由于 iteritems 将被删除,因此需要进行修改。


这对我来说失败了:<type 'exceptions.TypeError'>:iter()返回类型为'list'的非迭代器 - mike
3
不要使用汽车,因为未来我们将拥有悬浮滑板。 - J.J

7

现在在Python 2.7中也可以使用OrderedDict

>>> from collections import OrderedDict
>>> d = OrderedDict([('first', 1),
...                  ('second', 2),
...                  ('third', 3)])
>>> d.items()
[('first', 1), ('second', 2), ('third', 3)]

这里是2.7版本的新特性页面和OrderedDict API


2
这将按照插入的顺序返回键和值 - 不按排序顺序(即字母顺序)。 - Tony Suffolk 66

6

通常情况下,可以这样对字典进行排序:

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

针对问题中的特定情况,如果需要“drop in replacement”替代d.iteritems(),可以添加一个类似如下的函数:

def sortdict(d, **opts):
    # **opts so any currently supported sorted() options can be passed
    for k in sorted(d, **opts):
        yield k, d[k]

因此,结束行会发生变化

return dict.iteritems()

为了

return sortdict(dict)

或者

return sortdict(dict, reverse = True)

5
>>> import heapq
>>> d = {"c": 2, "b": 9, "a": 4, "d": 8}
>>> def iter_sorted(d):
        keys = list(d)
        heapq.heapify(keys) # Transforms to heap in O(N) time
        while keys:
            k = heapq.heappop(keys) # takes O(log n) time
            yield (k, d[k])


>>> i = iter_sorted(d)
>>> for x in i:
        print x


('a', 4)
('b', 9)
('c', 2)
('d', 8)

这种方法仍然具有O(N log N)的排序,但是在短暂的线性堆化后,它按顺序逐个返回项目,使得当您不总是需要整个列表时理论上更加高效。


4
如果您想按照插入顺序而不是键的顺序进行排序,您应该看一下Python的collections.OrderedDict。(仅适用于Python 3)

3

sorted返回一个列表,因此当您尝试迭代它时会出现错误,但由于您无法对字典进行排序,因此您必须处理列表。

我不知道您的代码的更大上下文是什么,但您可以尝试向结果列表添加一个迭代器。 像这样吗?

return iter(sorted(dict.iteritems()))

当然,现在你会得到元组,因为sorted将你的字典变成了一个元组列表。
例如: 假设你的字典是: {'a':1,'c':3,'b':2} 排序后它会变成一个列表:
[('a',1),('b',2),('c',3)]

所以当你实际迭代这个列表时,你会得到一个元组(在这个例子中)由一个字符串和一个整数组成,但至少你能够迭代它。


2
假设您正在使用CPython 2.x并且有一个大型字典mydict,那么使用sorted(mydict)将会很慢,因为sorted会构建一个已排序的mydict键的列表。
在这种情况下,您可能希望查看我的ordereddict包,其中包括C实现的sorteddict。特别是如果您必须在字典生命周期的不同阶段(即元素数量)多次遍历排序后的键列表。

http://anthon.home.xs4all.nl/Python/ordereddict/


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