Learning Python - Mark Lutz [212]
Dictionary View Iterators
As we saw briefly in Chapter 8, in Python 3.0 the dictionary keys, values, and items methods return iterable view objects that generate result items one at a time, instead of producing result lists all at once in memory. View items maintain the same physical ordering as that of the dictionary and reflect changes made to the underlying dictionary. Now that we know more about iterators, here’s the rest of the story:
>>> D = dict(a=1, b=2, c=3)
>>> D
{'a': 1, 'c': 3, 'b': 2}
>>> K = D.keys() # A view object in 3.0, not a list
>>> K
>>> next(K) # Views are not iterators themselves TypeError: dict_keys object is not an iterator >>> I = iter(K) # Views have an iterator, >>> next(I) # which can be used manually 'a' # but does not support len(), index >>> next(I) 'c' >>> for k in D.keys(): print(k, end=' ') # All iteration contexts use auto ... a c b As for all iterators, you can always force a 3.0 dictionary view to build a real list by passing it to the list built-in. However, this usually isn’t required except to display results interactively or to apply list operations like indexing: >>> K = D.keys() >>> list(K) # Can still force a real list if needed ['a', 'c', 'b'] >>> V = D.values() # Ditto for values() and items() views >>> V >>> list(V) [1, 3, 2] >>> list(D.items()) [('a', 1), ('c', 3), ('b', 2)] >>> for (k, v) in D.items(): print(k, v, end=' ') ... a 1 c 3 b 2 In addition, 3.0 dictionaries still have iterators themselves, which return successive keys. Thus, it’s not often necessary to call keys directly in this context: >>> D # Dictionaries still have own iterator {'a': 1, 'c': 3, 'b': 2} # Returns next key on each iteration >>> I = iter(D) >>> next(I) 'a' >>> next(I) 'c' >>> for key in D: print(key, end=' ') # Still no need to call keys() to iterate ... # But keys is an iterator in 3.0 too! a c b Finally, remember again that because keys no longer returns a list, the traditional coding pattern for scanning a dictionary by sorted keys won’t work in 3.0. Instead, convert keys views first with a list call, or use the sorted call on either a keys view or the dictionary itself, as follows: >>> D {'a': 1, 'c': 3, 'b': 2} >>> for k in sorted(D.keys())): print(k, D[k], end=' ') ... a 1 b 2 c 3 >>> D {'a': 1, 'c': 3, 'b': 2} >>> for k in sorted(D): print(k, D[k], end=' ') # Best practice key sorting ... a 1 b 2 c 3 Other Iterator Topics We’ll learn more about both list comprehensions and iterators in Chapter 20, in conjunction with functions, and again in Chapter 29 when we study classes. As you’ll see later: User-defined functions can be turned into iterable generator functions, with yield statements. List comprehensions morph into iterable generator expressions when coded in parentheses. User-defined classes are made iterable with __iter__ or __getitem__ operator overloading. In particular, user-defined iterators defined with classes allow arbitrary objects and operations to be used in any of the iteration contexts we’ve met here. Chapter Summary In this chapter, we explored concepts related to looping in Python. We took our first substantial look at the iteration protocol in Python—a way for nonsequence objects to take part in iteration loops—and at list comprehensions. As we saw, a list comprehension is an expression similar to a for loop that applies another expression to all the items in any iterable object. Along the way, we also saw other built-in iteration tools at work and studied recent iteration additions in Python 3.0. This wraps up our tour of specific procedural statements