Why does Python 3 need dict.items to be wrapped with list()?

Question:

I’m using Python 3. I’ve just installed a Python IDE and I am curious about the following code warning:

features = { ... }
for k, v in features.items():
    print("%s=%s" % (k, v))

Warning is: “For Python3 support should look like … list(features.items())

Also there is mention about this at http://docs.python.org/2/library/2to3.html#fixers

It also wraps existing usages of dict.items(), dict.keys(), and dict.values() in a call to list.

Why is this necessary?

Asked By: Dewfy

||

Answers:

In Python 3, dict.items(), dict.keys(), and dict.values() are iterators. Therefore if you are expecting a list, you might get some errors when doing operations that work on lists, but not necessarily on iterators, such as len(dict.items()) (will generate a TypeError).

CORRECTION

The dict_items returned by calling dict.items() in Python 3 does indeed have a __len__() and will not generate a TypeError. The dict_items object is not a list, however, and does not have list methods, such as append(), index(), etc…

Also, as the other (I would say much better) answers by Hamidi and Barnes state, dict_items is a view object that will dynamically change when the dict is altered.

Answered By: Joel Cornett

In Python 2, the methods items(), keys() and values() used to “take a snapshot” of the dictionary contents and return it as a list. It meant that if the dictionary changed while you were iterating over the list, the contents in the list would not change.

In Python 3, these methods return a view object whose contents change dynamically as the dictionary changes. Therefore, in order for the behavior of iterations over the result of these methods to remain consistent with previous versions, an additional call to list() has to be performed in Python 3 to “take a snapshot” of the view object contents.

Answered By: Frédéric Hamidi

Python 3 returns a Dictionary View Object rather than a list which Python 2 would return and some operators that you would expect may not be true – also a View Object will change if the underlying dictionary changes, (possibly in the code that you are iterating through which could cause some unwelcome surprises).

Answered By: Steve Barnes

You can safely ignore this “extra precautions” warning: your code will work the same even without list in both versions of Python. It would run differently if you needed a list (but this is not the case): in fact, features.items() is a list in Python 2, but a view in Python 3. They work the same when used as an iterable, as in your example.

Now, the Python 2 to Python 3 conversion tool 2to3 errs on the side of safety, and assumes that you really wanted a list when you use dict.items(). This may not be the case (as in the question), in which case dict.items() in Python 3 (no wrapping list) is better (faster, and less memory-consuming, since no list is built).

Concretely, this means that Python 2 code can explicitly iterate over the view: for k, v in features.viewitems() (which will be converted in Python 3 by 2to3 to features.items()). It looks like your IDE thinks that the code is Python 2, because your for statement is very good, in Python 3, so there should be no warning about Python 3 support.

Answered By: Eric O Lebigot

When converting a project to python 3 using 2to3, you can disable this by excluding the dict fixer for more concise output:

$ 2to3 -x dict *

Watch out for iteritems(), iterkeys() https://docs.python.org/2/library/2to3.html#2to3fixer-dict and fix by hand.

Answered By: fmalina
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.