13

dict keeps insertion order since Python 3.6 (see this).

OrderedDict was developed just for this purpose (before Python 3.6).

Since Python 3.6, is the key order always the same for dict or OrderedDict?

I wonder whether I can do this in my code and have always the same behavior (except of equality, and some extended methods in OrderedDict) but more efficiently:

if sys.version_info[:2] >= (3, 6):
  OrderedDict = dict
else:
  from collections import OrderedDict 

Or phrased differently, for Python >=3.6, is there any reason to use OrderedDict?

Albert
  • 65,406
  • 61
  • 242
  • 386
  • 1
    OrderedDict has methods that are not present in dict – Dani Mesejo Oct 26 '21 at 12:53
  • Why you want to do that, when you can just do `from collections import OrderedDict` also in newer versions? – Daweo Oct 26 '21 at 13:00
  • @Daweo: It would be much faster, right? – Albert Oct 26 '21 at 13:16
  • 1
    I think the practical question is why you would want to support Python 3.5 or earlier in the first place. [The oldest version still maintained is Python 3.6](https://www.python.org/downloads/), and even that is just security fixes. – MisterMiyagi Oct 26 '21 at 13:26
  • Does this answer your question: https://stackoverflow.com/questions/34305003/difference-between-dictionary-and-ordereddict – Dani Mesejo Oct 26 '21 at 13:28
  • 2
    @Albert [``OrderedDict`` has a C implementation since Python 3.5](https://bugs.python.org/issue16991), so it should be competitive to ``dict`` for many operations. – MisterMiyagi Oct 26 '21 at 13:28
  • @Albert then please test `OrderedDict` vs `dict` time consumptions for case which are most common in your usage – Daweo Oct 26 '21 at 13:47
  • @MisterMiyagi Looking at the [current C implementation](https://github.com/python/cpython/blob/main/Objects/odictobject.c), it seems to be more complex that `dict` by using a linked list to explicitly store the order. Why does it need that when `dict` already keeps the order? But anyway, that makes it more complex, and thus also slower. Not sure by how much. – Albert Oct 26 '21 at 14:26
  • @MisterMiyagi: Regarding Python version: So, what would be the reason since Python 3.6 to use `OrderedDict` at all then? And my code snippet is just to make my question more explicit. – Albert Oct 26 '21 at 14:28

3 Answers3

9

Both OrderedDict and dict are insertion-ordered¹ for iteration. There is practically no reason to use OrderedDict if iteration order is the only deciding point, especially if re-ordering is not needed.
Obviously, if comparison order is desired OrderedDict and dict are not interchangeable.

Or phrased differently, for Python >=3.6, is there any reason to use OrderedDict?

These days OrderedDict is to dict what deque is to list, basically. OrderedDict/deque are based on linked lists² whereas dict/list are based on arrays. The former have better pop/move/FIFO semantics, since items can be removed from the start/middle without moving other items.

Since arrays are generally very cache friendly, the linked list advantage only comes into play for very large containers. Also, OrderedDict (unlike deque) does not have guarantees for its linked list semantics and its advantage may thus not be portable. OrderedDict should primarily be used if many pop/move/FIFO operations are needed and benchmarking can compare the performance of dict vs. OrderedDict in practice.


¹This applies to all currently supported implementations compliant with the Python language spec, i.e. CPython and PyPy since Python 3.6.

²OrderedDict in CPython still preserves O(1) key access. This is realised by also having a "regular" lookup table, using the linked list for order between items and the lookup table for direct item access. It's complicated.

MisterMiyagi
  • 44,374
  • 10
  • 104
  • 119
  • The linked list in `OrderedDict` is just for storing the key order, not the mapping itself, which is just a normal `dict`. Although I wonder why `OrderedDict` actually needs to store the key order, if it is anyway the same. – Albert Oct 26 '21 at 18:57
  • @Albert The linked list order and item order (i.e. the ordering/storage of ``dict``) aren't the same when using ``move_to_end`` (and perhaps other methods). The linked list also allows *direct* access to the "first"/"last" item, whereas the ``dict`` item storage requires to scan for these to skip uninhabited slots. – MisterMiyagi Oct 27 '21 at 08:17
  • 1
    Ah right. Although these are things I would never need for my use case (the only thing I need is insertion order of keys, nothing else). So it very much sounds like I should just use `dict` then. `dict` even has the comparison just as I want it. – Albert Oct 27 '21 at 08:37
2

I realize that the different behavior of equality (__eq__) can be actually a major concern, why such code snippet is probably not good.

However, you could maybe still do this:

if sys.version_info[:2] >= (3, 6):
  OrderedDict = dict
else:
  from collections import OrderedDict as _OrderedDict

  class OrderedDict(_OrderedDict):
    __eq__ = dict.__eq__
    __ne__ = dict.__ne__

The difference is that with the original collections.OrderedDict, {1:1,2:2} is not the same as {2:2,1:1}, but for dict and my overwritten OrderedDict in this example, it is the same.

Albert
  • 65,406
  • 61
  • 242
  • 386
  • 1
    Might be worth expanding on this - some readers may not know the difference between equality of dicts vs. OrderedDicts. – kaya3 Oct 26 '21 at 15:25
1

Some behaviour remains the same, but OrderedDict are reversible and dict are not:

from collections import OrderedDict

d = { "a" : 1, "c" : 2}

od = OrderedDict(d.items())

print(list(reversed(od)))
print(list(reversed(d)))

Output

['c', 'a']

Traceback (most recent call last):
  File "path/to/file", line 8, in <module>
    print(list(reversed(d)))
TypeError: 'dict' object is not reversible

From the documentation:

In addition to the usual mapping methods, ordered dictionaries also support reverse iteration using reversed().

Dani Mesejo
  • 61,499
  • 6
  • 49
  • 76
  • 3
    Since Python 3.8, `reversed` does work on a regular `dict`. See [doc](https://docs.python.org/3/library/stdtypes.html#dict). – Albert Oct 26 '21 at 13:18
  • Can you be more specific on "some behaviour"? Which behavior exactly is different (except reversible in Python 3.6 and 3.7)? Or asked reversely, despite this, everything is the same? – Albert Oct 26 '21 at 13:19
  • When I was referring to some behaviour was the rest of it with exception of course of the methods present in OrderedDict that are not in dict. Out of curiosity, why do you want to do such a thing? – Dani Mesejo Oct 26 '21 at 13:21
  • 1
    Using `dict` should be much faster than `OrderedDict`. – Albert Oct 26 '21 at 13:22