Online Book Reader

Home Category

Learning Python - Mark Lutz [278]

By Root 1829 0
your own map(func, ...)

Although the map and zip built-ins are fast and convenient, it’s always possible to emulate them in code of our own. In the preceding chapter, for example, we saw a function that emulated the map built-in for a single sequence argument. It doesn’t take much more work to allow for multiple sequences, as the built-in does:

# map(func, seqs...) workalike with zip

def mymap(func, *seqs):

res = []

for args in zip(*seqs):

res.append(func(*args))

return res

print(mymap(abs, [−2, −1, 0, 1, 2]))

print(mymap(pow, [1, 2, 3], [2, 3, 4, 5]))

This version relies heavily upon the special *args argument-passing syntax—it collects multiple sequence (really, iterable) arguments, unpacks them as zip arguments to combine, and then unpacks the paired zip results as arguments to the passed-in function. That is, we’re using the fact that the zipping is essentially a nested operation in mapping. The test code at the bottom applies this to both one and two sequences to produce this output (the same we would get with the built-in map):

[2, 1, 0, 1, 2]

[1, 8, 81]

Really, though, the prior version exhibits the classic list comprehension pattern, building a list of operation results within a for loop. We can code our map more concisely as an equivalent one-line list comprehension:

# Using a list comprehension

def mymap(func, *seqs):

return [func(*args) for args in zip(*seqs)]

print(mymap(abs, [−2, −1, 0, 1, 2]))

print(mymap(pow, [1, 2, 3], [2, 3, 4, 5]))

When this is run the result is the same as before, but the code is more concise and might run faster (more on performance in the section Timing Iteration Alternatives). Both of the preceding mymap versions build result lists all at once, though, and this can waste memory for larger lists. Now that we know about generator functions and expressions, it’s simple to recode both these alternatives to produce results on demand instead:

# Using generators: yield and (...)

def mymap(func, *seqs):

res = []

for args in zip(*seqs):

yield func(*args)

def mymap(func, *seqs):

return (func(*args) for args in zip(*seqs))

These versions produce the same results but return generators designed to support the iteration protocol—the first yields one result at a time, and the second returns a generator expression’s result to do the same. They produce the same results if we wrap them in list calls to force them to produce their values all at once:

print(list(mymap(abs, [−2, −1, 0, 1, 2])))

print(list(mymap(pow, [1, 2, 3], [2, 3, 4, 5])))

No work is really done here until the list calls force the generators to run, by activating the iteration protocol. The generators returned by these functions themselves, as well as that returned by the Python 3.0 flavor of the zip built-in they use, produce results only on demand.

Coding your own zip(...) and map(None, ...)

Of course, much of the magic in the examples shown so far lies in their use of the zip built-in to pair arguments from multiple sequences. You’ll also note that our map workalikes are really emulating the behavior of the Python 3.0 map—they truncate at the length of the shortest sequence, and they do not support the notion of padding results when lengths differ, as map does in Python 2.X with a None argument:

C:\misc> c:\python26\python

>>> map(None, [1, 2, 3], [2, 3, 4, 5])

[(1, 2), (2, 3), (3, 4), (None, 5)]

>>> map(None, 'abc', 'xyz123')

[('a', 'x'), ('b', 'y'), ('c', 'z'), (None, '1'), (None, '2'), (None, '3')]

Using iteration tools, we can code workalikes that emulate both truncating zip and 2.6’s padding map—these turn out to be nearly the same in code:

# zip(seqs...) and 2.6 map(None, seqs...) workalikes

def myzip(*seqs):

seqs = [list(S) for S in seqs]

res = []

while all(seqs):

res.append(tuple(S.pop(0) for S in seqs))

return res

def mymapPad(*seqs, pad=None):

seqs = [list(S) for S in seqs]

res = []

while any(seqs):

res.append(tuple((S.pop(0) if S else pad) for S in seqs))

return res

S1, S2 = 'abc', 'xyz123'

print(myzip(S1, S2))

print(mymapPad(S1,

Return Main Page Previous Page Next Page

®Online Book Reader