Learning Python - Mark Lutz [200]
>>> map(None, S1, S2) # 2.X only
[('a', 'x'), ('b', 'y'), ('c', 'z'), (None, '1'), (None, '2'), (None,'3')]
This example is using a degenerate form of the map built-in, which is no longer supported in 3.0. Normally, map takes a function and one or more sequence arguments and collects the results of calling the function with parallel items taken from the sequence(s). We’ll study map in detail in Chapters 19 and 20, but as a brief example, the following maps the built-in ord function across each item in a string and collects the results (like zip, map is a value generator in 3.0 and so must be passed to list to collect all its results at once):
>>> list(map(ord, 'spam'))
[115, 112, 97, 109]
This works the same as the following loop statement, but is often quicker:
>>> res = []
>>> for c in 'spam': res.append(ord(c))
>>> res
[115, 112, 97, 109]
* * *
Note
Version skew note: The degenerate form of map using a function argument of None is no longer supported in Python 3.0, because it largely overlaps with zip (and was, frankly, a bit at odds with map’s function-application purpose). In 3.0, either use zip or write loop code to pad results yourself. We’ll see how to do this in Chapter 20, after we’ve had a chance to study some additional iteration concepts.
* * *
Dictionary construction with zip
In Chapter 8, I suggested that the zip call used here can also be handy for generating dictionaries when the sets of keys and values must be computed at runtime. Now that we’re becoming proficient with zip, I’ll explain how it relates to dictionary construction. As you’ve learned, you can always create a dictionary by coding a dictionary literal, or by assigning to keys over time:
>>> D1 = {'spam':1, 'eggs':3, 'toast':5}
>>> D1
{'toast': 5, 'eggs': 3, 'spam': 1}
>>> D1 = {}
>>> D1['spam'] = 1
>>> D1['eggs'] = 3
>>> D1['toast'] = 5
What to do, though, if your program obtains dictionary keys and values in lists at runtime, after you’ve coded your script? For example, say you had the following keys and values lists:
>>> keys = ['spam', 'eggs', 'toast']
>>> vals = [1, 3, 5]
One solution for turning those lists into a dictionary would be to zip the lists and step through them in parallel with a for loop:
>>> list(zip(keys, vals))
[('spam', 1), ('eggs', 3), ('toast', 5)]
>>> D2 = {}
>>> for (k, v) in zip(keys, vals): D2[k] = v
...
>>> D2
{'toast': 5, 'eggs': 3, 'spam': 1}
It turns out, though, that in Python 2.2 and later you can skip the for loop altogether and simply pass the zipped keys/values lists to the built-in dict constructor call:
>>> keys = ['spam', 'eggs', 'toast']
>>> vals = [1, 3, 5]
>>> D3 = dict(zip(keys, vals))
>>> D3
{'toast': 5, 'eggs': 3, 'spam': 1}
The built-in name dict is really a type name in Python (you’ll learn more about type names, and subclassing them, in Chapter 31). Calling it achieves something like a list-to-dictionary conversion, but it’s really an object construction request. In the next chapter we’ll explore a related but richer concept, the list comprehension, which builds lists in a single expression; we’ll also revisit 3.0 dictionary comprehensions an alternative to the dict cal for zipped key/value pairs.
Generating Both Offsets and Items: enumerate
Earlier, we discussed using range to generate the offsets of items in a string, rather than the items at those offsets. In some programs, though, we need both: the item to use, plus an offset as we go. Traditionally, this was coded with a simple for loop that also kept a counter of the current offset:
>>> S = 'spam'
>>> offset = 0
>>> for item in S:
... print(item, 'appears at offset', offset)
... offset += 1
...
s appears at offset 0
p appears at offset 1
a appears at offset 2
m appears at offset 3
This works, but in recent Python releases a new built-in named enumerate does the job for us:
>>> S = 'spam'
>>> for (offset, item) in enumerate(S):
... print(item, 'appears at offset', offset)
...
s appears at offset 0
p appears at offset 1
a appears