Learning Python - Mark Lutz [210]
1&2&3&4
>>> f(*open('script1.py')) # Iterates by lines too!
import sys
&print(sys.path)
&x = 2
&print(2 ** 33)
In fact, because this argument-unpacking syntax in calls accepts iterables, it’s also possible to use the zip built-in to unzip zipped tuples, by making prior or nested zip results arguments for another zip call (warning: you probably shouldn’t read the following example if you plan to operate heavy machinery anytime soon!):
>>> X = (1, 2)
>>> Y = (3, 4)
>>>
>>> list(zip(X, Y)) # Zip tuples: returns an iterable
[(1, 3), (2, 4)]
>>>
>>> A, B = zip(*zip(X, Y)) # Unzip a zip!
>>> A
(1, 2)
>>> B
(3, 4)
Still other tools in Python, such as the range built-in and dictionary view objects, return iterables instead of processing them. To see how these have been absorbed into the iteration protocol in Python 3.0 as well, we need to move on to the next section.
New Iterables in Python 3.0
One of the fundamental changes in Python 3.0 is that it has a stronger emphasis on iterators than 2.X. In addition to the iterators associated with built-in types such as files and dictionaries, the dictionary methods keys, values, and items return iterable objects in Python 3.0, as do the built-in functions range, map, zip, and filter. As shown in the prior section, the last three of these functions both return iterators and process them. All of these tools produce results on demand in Python 3.0, instead of constructing result lists as they do in 2.6.
Although this saves memory space, it can impact your coding styles in some contexts. In various places in this book so far, for example, we’ve had to wrap up various function and method call results in a list(...) call in order to force them to produce all their results at once:
>>> zip('abc', 'xyz') # An iterable in Python 3.0 (a list in 2.6)
>>> list(zip('abc', 'xyz')) # Force list of results in 3.0 to display [('a', 'x'), ('b', 'y'), ('c', 'z')] This isn’t required in 2.6, because functions like zip return lists of results. In 3.0, though, they return iterable objects, producing results on demand. This means extra typing is required to display the results at the interactive prompt (and possibly in some other contexts), but it’s an asset in larger programs—delayed evaluation like this conserves memory and avoids pauses while large result lists are computed. Let’s take a quick look at some of the new 3.0 iterables in action. The range Iterator We studied the range built-in’s basic behavior in the prior chapter. In 3.0, it returns an iterator that generates numbers in the range on demand, instead of building the result list in memory. This subsumes the older 2.X xrange (see the upcoming version skew note), and you must use list(range(...)) to force an actual range list if one is needed (e.g., to display results): C:\\misc> c:\python30\python >>> R = range(10) # range returns an iterator, not a list >>> R range(0, 10) >>> I = iter(R) # Make an iterator from the range >>> next(I) # Advance to next result 0 # What happens in for loops, comprehensions, etc. >>> next(I) 1 >>> next(I) 2 >>> list(range(10)) # To force a list if required [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Unlike the list returned by this call in 2.X, range objects in 3.0 support only iteration, indexing, and the len function. They do not support any other sequence operations (use list(...) if you require more list tools): >>> len(R) # range also does len and indexing, but no others 10 >>> R[0] 0 >>> R[-1] 9 >>> next(I) # Continue taking from iterator, where left off 3 >>> I.__next__() # .next() becomes .__next__(), but use new next() 4 * * * Note
Version skew note: Python 2.X also has a built-in called xrange, which is like range but produces items on demand instead of building a list of results in memory all at once. Since this is exactly what the new iterator-based range does in Python 3.0, xrange is no longer available in 3.0—it has been subsumed. You may still see it in 2.X code, though,