is the python dict str() function reliably sorting keys?

Question:

in python, both the dict {1:1,2:2,3:3} and {3:3,2:2,1:1} produces "{1:1,2:2,3:3}" when str()’d?

Can I rely on this sorting, or at least on the fact that dicts containing the same key/valuepairs will generate the same string when put through the str() function?

Asked By: bigblind

||

Answers:

You can rely on neither of these two properties. The order of a dictionary when converted to a string depends also on the insertion order of the key/value pairs.

With a bit of knowledge of the Python source code (watch The Mighty Dictionary from PyCon 2010), or a bit of trial and error, you can easily find counter examples:

>>> {1: 1, 9: 9}
{1: 1, 9: 9}
>>> {9: 9, 1: 1}
{9: 9, 1: 1}
Answered By: Sven Marnach

The dict built-in type does not guarantee any particular order of the keys.

Even if it seems that you always get the same string, don’t rely on this. If you do, when you upgrade Python there might be a change in the implementation which causes your assumption to fail.

The OrderedDict class does provide guarantees about the order of the keys.

Answered By: Mark Byers

No, you cannot rely on that. As mentioned in Learning Python. 4th Edition by Mark Lutz (page 94.):

(…) because dictionaries are not sequences, they don’t maintain any
dependable left-to-right order
. This means that if we make a dictionary and print it
back, its keys may come back in a different order than that in which we typed them (…)

However, the book mentions another solution for printing key-value pairs in the order of keys (D is the dictionary you want to pring):

>>> for key in sorted(D):
    print(key, '=>', D[key])

By using the above, you can print items in any way you like, or even create some sequence containing ordered elements, like that:

>>> D = {'a': 12, 'b': 65, 7: 'asd'}
>>> S = [(key, D[key]) for key in sorted(D)]
>>> S
[(7, 'asd'), ('a', 12), ('b', 65)]

where order of items in S is dependable (you can depend on it, as it will not change until you explicitly change it).

Answered By: Tadeck

No, you can’t. Try this:

{ i:i for i in range(0, 100, 10) }

The reason it works for contiguous integers starting at zero is that each integer hashes to itself (hash(i) == i) and dictionaries will size their internal tables to be at least as large as the elements they hold (they use a probing strategy, which requires this). Consequently, integer i ends up in slot i with no collisions. You’ll also find that contiguous integers starting at some other number tend to be monotonically increasing as well, but they may wrap around somewhere in the middle:

>>> { i:'' for i in range(25, 35) }
{32: '', 33: '', 34: '', 25: '', 26: '', 27: '', 28: '', 29: '', 30: '', 31: ''}

It is important to note that these are merely observations of actual behaviour. Nothing in the language guarantees any of this, so you can’t rely on it.

Answered By: Marcelo Cantos
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.