Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are python dictionaries not reversible for python3.7?

Starting from 3.7, standard python dictionaries are guaranteed to maintain the insertion order. (*)

d = {'b': 1, 'a': 2}
for k in d: 
    print(k)
# Prints always 'b' before 'a'.

In other words, the dict keys are kept in a strict order. In principle, this would allow the keys to be reversible. However, none of the following works:

# TypeError: 'dict' object is not reversible
for k in reversed(d): 
    print(k)

# TypeError: 'dict_keys' object is not reversible
for k in reversed(d.keys()): 
    print(k)

Questions: What is the reasoning behind this behavior? Why have dicts not been made reversible? Are there any plans to change this behavior in future?

The workaround of course works:

for k in reversed(list(d.keys())): 
    print(k)

(*) As a matter of fact, this is the case already for typical installations of python 3.6, as discussed in this post.


Update: Starting with python 3.8 dicts are actually reversible. The accepted answer refers to the discussion between Guido and other core developers that led to this decision. In a nutshell, they weighted language consistency against implementation efforts and actual benefits for the users.

like image 449
normanius Avatar asked Oct 16 '19 12:10

normanius


People also ask

Can a dictionary be reversed in Python?

Instead of using a for loop, we can reverse a dictionary in a single python statement using dictionary comprehension. Here, we will create the output dictionary by reversing the key and value in each key-value pair of the dictionary as follows.

How do I reverse a dictionary order in Python?

Use dict. items() to get a list of tuple pairs from d and sort it using a lambda function and sorted(). Use dict() to convert the sorted list back to a dictionary. Use the reverse parameter in sorted() to sort the dictionary in reverse order, based on the second argument.

Why are dictionaries not ordered Python?

Should I use dict or OrderedDict in Python 3.6? dict is not explicitly meant to be an ordered collection, so if you want to stay consistent and not rely on a side effect of the new implementation you should stick with OrderedDict .

Does Python dict keep order?

Since dictionaries in Python 3.5 don't remember the order of their items, you don't know the order in the resulting ordered dictionary until the object is created. From this point on, the order is maintained.


1 Answers

From the docs:

reversed(seq)

Return a reverse iterator. seq must be an object which has a __reversed__() method or supports the sequence protocol (the __len__() method and the __getitem__() method with integer arguments starting at 0).

A dict object doesn't implement __reversed__. It does implement both of the latter methods. However, __getitem__ takes keys as arguments, rather than integers (starting at 0).

As to why, this has already been suggested and discussed here.

EDIT:

These quotes are from Python-Dev mailing list (thread "Add __reversed__ methods for dict", started on 25. 05. 18), I'll start with the "conceptual" arguments, first one is from Antoine Pitrou:

It's worth nothing that OrderedDict already supports reversed(). The argument could go both ways:

  1. dict is similar to OrderedDict nowadays, so it should support reversed() too;

  2. you can use OrderedDict to signal explicitly that you care about ordering; no need to add anything to dict.

My thought is that guaranteed insertion order for regular dicts is brand new, so it will take a while for the notion settle in and become part of everyday thinking about dicts. Once that happens, it is probably inevitable that use cases will emerge and that __reversed__ will get added at some point. The implementation seems straightforward and it isn't much of a conceptual leap to expect that a finite ordered collection would be reversible.

Followed by Raymond Hettinger's reply:

Given that dicts now track insertion order, it seems reasonable to want to know the most recent insertions (i.e. looping over the most recently added tasks in a task dict). Other possible use cases will likely correspond to how we use the Unix tail command.

If those use cases arise, it would be nice for __reversed__ to already be supported so that people won't be tempted to implement an ugly workaround using popitem() calls followed by reinsertions.

The main concern expressed in the mailing list was that this would add too much bloat or reduce memory efficiency (having to have doubly linked lists instead of singly linked ones) in at least some implementations, here's Inada Naoki's quote from Python bug tracker (issue 33462):

"Have an order" doesn't mean "reversible". For example, single linked list is ordered, but not reversible.

While CPython implementation can provide efficient __reverse__, adding __reverse__ means all Python implementation is expected to provide it. For example, some Python implementation may be able to implement dict with hashmap + single linked list. If __reverse__ is added, it's not possible anymore.

Back to the mailing list, here are the two last messages (both posted on 08.06.2018). First is from Michael Selik:

Am I correct in saying that the consensus is +1 for inclusion in v3.8?

The last point in the thread was INADA Naoki researching various implementations and deciding that it's OK to include this feature in 3.8. As I understand it, Guido was in agreement with INADA's advice to wait for MicroPython's implementation of v3.7. Since INADA has changed minds, I'm guessing it's all in favor?

Concluding with Guido van Rossum's message:

That sounds right to me. We will then have had two versions where this was the case:

  • 3.6 where order preserving was implemented in CPython but in the language spec

  • 3.7 where it was also added to the language spec

As noted in the other answer and comments, reversed() is supported for both dicts and dictviews since version 3.8 (14.10.2018).

like image 196
gstukelj Avatar answered Sep 22 '22 13:09

gstukelj