我知道Python字典是无序的,但为什么我总是得到相同顺序的键?

1 投票
4 回答
1336 浏览
提问于 2025-04-18 11:32

我有一个叫做 tmp 的字典:

>>> tmp
{1: 'ONE', 2: 'TWO', 3: 'THREE'}
>>> type(tmp)
<type 'dict'>

我对这个字典进行了二十次循环,每次循环得到的键的顺序都是一样的。

>>> for i in range(20):
...   print tmp.keys()
... 
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]

我知道在 Python 中,字典是无序的,但是为什么我每次得到的键的顺序总是一样呢?

4 个回答

1

因为Python的字典是用哈希表来实现的,所以键的顺序是根据它的哈希值来决定的,哈希值决定了存储“键/值”对的位置。

如果用整数作为键,那么哈希值就是这个整数本身。

>>> map(hash, [1,2,3])
[1,2,3]

在这里,你使用的键是连续的整数,它们的哈希值也是连续的,这样底层的哈希算法很可能会选择连续的位置来存储“键/值”对,所以你看到的顺序和你创建字典时的顺序是一样的。这里有一个简单的反例:

>>> tmp = {1:1, 5:5, 10:10}
>>> tmp.keys()
[1, 10, 5]

当然,在循环过程中,你不要修改字典,这样就不会导致哈希表的大小改变,所以顺序就保持不变。

>>> for i in range(5):
...   print tmp.keys()
[1, 10, 5]
[1, 10, 5]
[1, 10, 5]
[1, 10, 5]
[1, 10, 5]

如果你对Python的具体实现感兴趣,可以看看这篇不错的文章: Python字典实现

1

在创建一个字典时,Python会自己决定一个顺序。每次你调用 .keys().values().items() 时,只要你没有修改这个字典,得到的结果顺序都会是一样的。希望这样说能让你明白。

如下面的例子所示,尽管我创建字典时是按照特定的顺序,但Python会重新排列这个字典。不过,之后只要你不修改字典,顺序就会一直保持不变。

a = {'apple' : 'fruit', 'car' : 'vehicle', 'onion' : 'vegetable'}

print a

for i in range(5):
    print a.keys()

[OUTPUT]
{'car': 'vehicle', 'apple': 'fruit', 'onion': 'vegetable'}
['car', 'apple', 'onion']
['car', 'apple', 'onion']
['car', 'apple', 'onion']
['car', 'apple', 'onion']
['car', 'apple', 'onion']
2

因为如果不这样做,这个就不管用了:

for key, value in zip(d.keys(), d.values()):
    ...

还有其他类似的问题。

在Python中,字典(dict)和集合(set)的遍历顺序是有保证的,只要这些字典或集合没有被修改。你可以试着往tmp里添加其他值,下一次你可能会发现顺序变了。

(不过,要注意用小整数作为键并不是一个特别好的例子,因为在CPython中,小整数会直接映射到它们自己,所以它们更有可能按数字顺序出现。你可以试试用一些字符串。)

4

在你调用 tmp.keys() 之间,你并没有修改 tmp,所以键的顺序始终不会改变。

根据文档:

如果在调用 items()keys()values()iteritems()iterkeys()itervalues() 这些方法时,没有对字典进行任何修改,那么返回的列表会直接对应。这意味着你可以使用 zip() 来创建 (值, 键) 的配对,比如 pairs = zip(d.values(), d.keys())。对于 iterkeys()itervalues() 方法也是一样的关系:pairs = zip(d.itervalues(), d.iterkeys()) 也会提供相同的配对值。另一种创建相同列表的方法是 pairs = [(v, k) for (k, v) in d.iteritems()]

撰写回答