Unwrapping data while mapping

When we use a construct, such as (f(x) for x, y in C), we use multiple assignments in the for statement to unwrap a multi-valued tuple and then apply a function. The whole expression is a mapping. This is a common Python optimization to change the structure and apply a function.

We'll use our trip data from Chapter 4, Working with Collections. The following is a concrete example of unwrapping while mapping:

from typing import Callable, Iterable, Tuple, Iterator, Any

Conv_F = Callable[[float], float]
Leg = Tuple[Any, Any, float]

def convert(
conversion: Conv_F,
trip: Iterable[Leg]) -> Iterator[float]:
return (
conversion(distance) for start, end, distance in trip
)

This higher-order function would be supported by conversion functions that we can apply to our raw data, as follows:

to_miles = lambda nm: nm*5280/6076.12
to_km = lambda nm: nm*1.852
to_nm = lambda nm: nm  

This function would then be used, as follows, to extract distance and apply a conversion function:

convert(to_miles, trip)  

As we're unwrapping, the result will be a sequence of floating-point values. The results are as follows:

[20.397120559090908, 35.37291511060606, ..., 44.652462240151515]  

This convert() function is highly specific to our start-end-distance trip data structure, as the for loop decomposes that three tuple.

We can build a more general solution for this kind of unwrapping while mapping a design pattern. It suffers from being a bit more complex. First, we need general-purpose decomposition functions, as in the following code snippet:

fst = lambda x: x[0]
snd = lambda x: x[1]
sel2 = lambda x: x[2]  

We'd like to be able to express f(sel2(s_e_d)) for s_e_d in trip. This involves functional composition; we're combining a function, such as to_miles(), and a selector, such as sel2(). We can express functional composition in Python using yet another lambda, as follows:

to_miles = lambda s_e_d: to_miles(sel2(s_e_d))

This gives us a longer but more general version of unwrapping, as follows:

(to_miles(s_e_d) for s_e_d in trip)

While this second version is somewhat more general, it doesn't seem wonderfully helpful. 

What's important to note about our higher-order convert() function is that we're accepting a function as an argument and returning a generator function as a result. The convert() function is not a generator function; it doesn't yield anything. The result of the convert() function is a generator expression that must be evaluated to accumulate the individual values. We've used Iterator[float] to emphasize that the result is an iterator; a subclass of Python's generator functions.

The same design principle works to create hybrid filters instead of mappings. We'd apply the filter in an if clause of the generator expression that was returned.

We can combine mapping and filtering to create yet more complex functions. While it is appealing to create more complex functions, it isn't always valuable. A complex function might not beat the performance of a nested use of simple map() and filter() functions. Generally, we only want to create a more complex function if it encapsulates a concept and makes the software easier to understand.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset