5

Do dict literals keep the order of their keys, in Python 3.7+? For example, is it guaranteed that {1: "one", 2: "two"} will always have its keys ordered this way (1, then 2) when iterating over it? (There is a thread in the Python mailing list with a similar subject, but it goes in all directions and I couldn't find an answer.)

Similarly, is a dictionary like dict([('sape', 4139), ('guido', 4127), ('jack', 4098)]) ordered like in the list?

The same question is true for other naturally ordered constructions, like dict comprehension and dict(sape=4139, guido=4127, jack=4098).

PS: it is documented that dictionaries preserve insertion order. This question thus essentially asks: is it guaranteed that data is inserted in the order of a dict literal, of the list given to dict(), etc.

Eric O. Lebigot
  • 91,433
  • 48
  • 218
  • 260
  • It depends on the order of insertion. See https://stackoverflow.com/questions/39980323/are-dictionaries-ordered-in-python-3-6. – Marcel Sep 13 '20 at 16:34
  • You're reading a thread from before they made the final decision on dict order preservation. They're order-preserving now. – user2357112 Sep 13 '20 at 16:36
  • 1
    So the question is whether dict literals, etc. insert in the order of the source code, etc. One could guess that yes, but is it guaranteed by the language? – Eric O. Lebigot Sep 13 '20 at 19:57
  • Insertion order preserved as cpython implementation detail https://docs.python.org/3.7/whatsnew/3.6.html#new-dict-implementation, as language invariant https://docs.python.org/3.7/whatsnew/3.7.html#summary-release-highlights – snakecharmerb Sep 13 '20 at 20:02
  • 3
    Does this answer your question? [Are dictionaries ordered in Python 3.6+?](https://stackoverflow.com/questions/39980323/are-dictionaries-ordered-in-python-3-6) – wjandrea Sep 13 '20 at 21:00
  • 1
    Oops, I missed the PS and thought you weren't aware they're insertion-ordered. Instead, does this answer your question? [How to keep keys/values in same order as declared?](https://stackoverflow.com/q/1867861/4518341) – wjandrea Sep 13 '20 at 21:04
  • A comment to Martijn Pieters's answer points to a [reference](https://docs.python.org/3/reference/expressions.html#dictionary-displays) that settles the case of the literal and of the comprehension: they are ordered as it appears. There is still the case of `dict([…])` and of `dict(x=…)`, but we're getting close! Don't hesitate to write an answer with this information! – Eric O. Lebigot Sep 13 '20 at 21:11
  • @EricOLebigot: The `dict(x=` bit is insertion order preserving by the earlier, pre-guaranteed `dict` ordering guarantee for keyword arguments `dict`s; they're received in the same order they're named when passed (they made that guarantee earlier than for `dict`s in general, because those are *always* new `dict`s, and there are no compromises required to keep insertion ordering like there are with regular `dict`s that undergo deletion and reinsertion and the like). – ShadowRanger Sep 13 '20 at 22:27
  • @ShadowRanger: what "earlier ordering guarantee for keyword arguments" are you referring to? – Eric O. Lebigot Sep 14 '20 at 21:39
  • 1
    @EricOLebigot: The results of PEP 520 and PEP 468, both included in [the 3.6 What's New docs](https://docs.python.org/3/whatsnew/3.6.html#pep-520-preserving-class-attribute-definition-order). 520 guaranteed the order of class attribute definitions, 468 preserved keyword argument order. Both were side-effects of the new compact `dict` implementation, but unlike the general order-preserving aspects of `dict`s, they were made part of the language spec, not just an implementation detail, in 3.6. – ShadowRanger Sep 14 '20 at 23:32
  • Useful to know! I added these points to wjandrea's answer, so as to give a full answer to the original question. – Eric O. Lebigot Sep 15 '20 at 20:20

1 Answers1

6

Yes, any method of constructing a dict preserves insertion order, in Python 3.7+.


For literal key-value pairs, see the documentation:

If a comma-separated sequence of key/datum pairs is given, they are evaluated from left to right to define the entries of the dictionary

For comprehensions, from the same source:

When the comprehension is run, the resulting key and value elements are inserted in the new dictionary in the order they are produced.

Lastly, the dict initializer works by iterating over its argument and keyword arguments, and inserting each in order, similar to this:

def __init__(self, mapping_or_iterable, **kwargs):
    if hasattr(mapping_or_iterable, "items"):  # It's a mapping
        for k, v in mapping_or_iterable.items():
            self[k] = v
    else:  # It's an iterable of key-value pairs
        for k, v in mapping_or_iterable:
            self[k] = v

    for k, v in kwargs.items():
        self[k] = v

[This is based on the source code, but glossing over a lot of unimportant details, e.g. that dict_init is just a wrapper on dict_update_common.]

This, combined with the fact that keyword arguments pass a dictionary in the same order since Python 3.6, makes dict(x=…, y=…) preserve the order of the variables.

wjandrea
  • 28,235
  • 9
  • 60
  • 81
  • 2
    Nitpick: It looks for the `keys` method to identify mappings, not `items`. It then uses `items`-like iteration for `dict` subclasses that haven't overridden `__iter__`; for everything else, it iterates the keys and looks up the values. Using `items` would make much more sense, but I suspect historical inertia keeps it using `keys`. None of this invalidates your answer, thus, mere nitpicking. – ShadowRanger Sep 13 '20 at 22:23
  • @Shadow Yeah, like I said, I'm glossing over a lot of unimportant details. That's good to know if you're implementing your own mapping-like class, but I just wanted to give a simple, high-level idea of how it works. – wjandrea Sep 13 '20 at 22:26