10. Collections and Iterators
Python 3 is known to avoid returning lists and dictionaries. If k is a dictionary, in Python 2, k.keys() returns a list of the keys in the dictionary, while in Python 3, k.keys() enables iteration over the keys in something like a for loop. Similarly, k.values() and k.items() returns lists of values or key-value pairs in Python 2, while in Python 3 we can only iterate over the values in a for loop. Python 3 only contains range and it behaves like the Python 2s xrange and in Python 3, the built-in map behaves like Python 2s itertools.imap . Therefore in Python 2 and 3, to get the old map behavior, we would instead use list(map(...)) . This chapter discusses compatibility for dictionaries, range, and map functions.
Iterable Dictionary Members
I need to mention that there is a difference in how the dict.keys() , dict.values() , and dict.items() operations work between Python 2 and 3. When adapting Python 2 code to Python 3, we get a TypeError when we try to operate on keys() , values() , and items() as lists.
Keys
In Python 3, dict.keys() does not return a list like in Python 2. It instead returns a view object. The returned dict_keys object is more like a set than a list. It actually returns iterable keys of a given dictionary.
>>> mydict = { 'echo': "lima", 'again': "golf" }
>>> type(mydict .keys())
>>> mydict.keys()
dict_keys(['echo', 'again'])
There is a dict.iterkeys() method in Python 2 to return iterable dictionary keys and this method works the same way dict.keys() works. To get iterable keys in both Python 2 and 3, we use six.iterkeys .
Download CollectionsandIterators/six_keys.py
import six
mydict = { 'echo': "lima", 'again': "golf" }
for key in six.iterkeys(mydict):
do_stuff(key)
The six.iterkeys() method replaces dictionary.iterkeys() in Python 2 and dictionary.keys() in Python 3.
There is also another neutral way of getting iterable keys by referring to the dictionary itself. For example, this Python 2 code:
Download CollectionsandIterators/keyspy2.py
mydict = { 'echo': "lima", 'again': "golf" }
for key in mydict.iterkeys():
do_stuff(key)
is the same as this:
Download CollectionsandIterators/keyspy2and3.py
mydict = { 'echo': "lima", 'again': "golf" }
for key in mydict:
do_stuff(key)
This works in both Python 2 and 3.
In summary, for neutral compatibility for iterable dictionary keys:
Use six.iterkeys() , which replaces dictionary.keys() in Python 3 and dictionary.iterkeys() in Python 2.
You can also refer to the dictionary to get iterable keys.
Values
In Python 2, if we want to work with an iterable of dictionary values, then we write code that looks like this:
Download CollectionsandIterators/valuespy2.py
mydict = { 'echo': "lima", 'again': "golf" }
for value in heights.itervalues():
do_stuff(value)
We call itervalues() in the dictionary. In contrast, in Python 3 we have to call values() in the dictionary to achieve the same.
Download CollectionsandIterators/valuespy3.py
mydict = { 'echo': "lima", 'again': "golf" }
for value in heights.values():
do_stuff(value)
To support both Python 2 and 3, we have to find a middle ground using one of these options. Use:
itervalues() from Python-futures builtins Module
The itervalues() method takes the dictionary as a parameter.
Download CollectionsandIterators/valuesbuiltins.py
from builtins import itervalues
mydict = { 'echo': "lima", 'again': "golf" }
for value in itervalues(mydict):
do_stuff(value)
itervalues() from six
Like itervalues() from futures builtins, the itervalues() method from six also takes the dictionary as a parameter and returns iterable values of the dictionary.
Download CollectionsandIterators/values_six.py
from six import itervalues
mydict = { 'echo': "lima", 'again': "golf" }
for value in itervalues(mydict):
do_stuff(value)
The itervalues() method replaces dictionary.itervalues() in Python 2 and dictionary.values() in Python 3.
In summary, for neutral compatibility for iterable dictionary values:
Call six.itervalues() with the dictionary as argument. This replaces dictionary.values in Python 3 and dictionary.itervalues() in Python 2.
You can also call itervalues() from futures builtins module with the dictionary as argument.
Items
Similarly, dictionary.items() returns a list of dictionary items in Python 2 and dict_item view objects. To get iterable items in Python 2, we use dict.iteritems() .
Download CollectionsandIterators/itemspy2.py
mydict = { 'echo': "lima", 'again': "golf" }
for (key, value) in heights.iteritems():
do_stuff(key, value)
While in Python 3, we use dict.items() .
Download CollectionsandIterators/itemspy3.py
mydict = { 'echo': "lima", 'again': "golf" }
for (key, value) in heights.items():
do_stuff(key, value)
There are a couple of ways we can achieve compatibility. future and six have wrappers for this.
iteritems() future Wrapper
We can use the iteritems() method from future to get iterable items. Importing the iteritems module from future overrides Python 2s iteritems() method functionality and Python 3s items() functionality.
Download CollectionsandIterators/items_future.py
from future.utils import iteritems
mydict = { 'echo': "lima", 'again': "golf" }
for (key, value) in initeritems(mydict):
do_stuff(key, value)
iteritems() six Wrapper
Similarly, six.iteritems() replaces dictionary.items() in Python 3 and dictionary.iteritems() in Python 2.
Download CollectionsandIterators/items_future.py
from future.utils import iteritems
mydict = { 'echo': "lima", 'again': "golf" }
for (key, value) in iteritems(mydict):
do_stuff(key, value)