Search code examples
pythoncpython

Does CPython guarantee that dict.keys has the same order as dict.values?


Does CPython guarantee that dict.keys() has the same order as dict.values() on an unmodified dict?

In other words, is it always true that:

list(d.items()) == list(zip(d.keys(), d.values()))

The docs say:

Keys and values are listed in an arbitrary order [...] If items(), keys(), values(), iteritems(), iterkeys(), and itervalues() are called with no intervening modifications to the dictionary, the lists will directly correspond.

However, the CPython source code suggests otherwise. The implementation of dict.items() in dictobject.c:

/* Preallocate the list of tuples, to avoid allocations during
 * the loop over the items, which could trigger GC, which
 * could resize the dict. :-(
 */

So, is it true that dict.keys() and dict.values() on an unmodified dict will always return items in the same order? Even if the GC runs?

If it is true, does this apply just CPython, or just Python 2?


Solution

  • Yes, it is still true, because dict.keys() and dict.values() do not need to allocate new objects. You are given a list object with references to the existing key or value objects, respectively, instead.

    On the other hand, dict.items() needs to create tuple objects to hold your key-value pairs. Hence that comment.

    Note that running the GC itself does not resize a dictionary; dicitonaries are only ever resized when they run out of space for new keys. But triggering a GC can trigger a __del__ handler, which could add to the dictionary, which could resize.. So it is the possibility that other Python code is triggered that could alter the dictionary that is prevented here.