Slicing
We already saw how you can access items in a list using the list[index]
syntax. This is the simplest case: it gives you a single item. The more general form of the syntax allows you to extract a contiguous subset of the list.
You can specify the index (starting at 0, remember) of the first item (i.e. inclusive), and the index of the item one past the last one you want (i.e. exclusive). For example l[0:len(l)]
will give you the entire list.
l = ['a', 'b', 'c', 'd', 'e', 'f', 'g'] l[3] #'d' l[3:6] #['d', 'e', 'f'] l[0:len(l)] #['a', 'b', 'c', 'd', 'e', 'f', 'g']
If you are interested in items from the first or last of the list, it can be simplified to omit the 0
or len(l)
:
l[:4] #['a', 'b', 'c', 'd'] l[4:] #['e', 'f', 'g']
Filtering
The filter
function takes a predicate (i.e. a function that returns a boolean value) and a list. filter
returns a filter object that generates a list that contains only those elements from the original list for which the predicate returns True
. Filter predicates can either be a reference to a named function or, quite often, a lambda.
Say you want to pick out only the numbers in a list that are multiples of 3. If a
is your original list of numbers, say the result of range(20)
:
a = range(20) list(filter(lambda x: x % 3 == 0, a)) #[0, 3, 6, 9, 12, 15, 18]
Notice the need to use list
to convert the filter object to a list.
Mapping
Another common thing to do with lists is to perform some operation on each item, accumulating the results into a new list. The map
function does this. It takes a function (which is often a lambda) and the original list. Similar to filter
, map
returns a map object.
Let's take the same list a
, and generate a list of the squares of its contents.
list(map(lambda x: x**2, a)) #[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361]
map
isn't limited to processing a single list; you can pass it any number of lists, with the restriction that the function has to have that number of parameters.
list(map(lambda x, y: x + y, [0,1,2,3], [4,5,6,7])) #[4, 6, 8, 10]
We can combine map
with filter
to process a subset of a list. Let's get the squares of the multiple of 3:
list(map(lambda x: x**2, filter(lambda x: x % 3 == 0, a))) #[0, 9, 36, 81, 144, 225, 324]
While this works, it can be a bit cumbersome at times. Thankfully Python provides a better alternative, which we will examine shortly.
Reducing
The third function we're going to look at is reduce
. It's different than filter
and map
in that it doesn't result in a sequence of values. Instead it takes a list, a function, and an optional initial value and uses them to compute a single value. It, in essence, reduces the list to a single value.
Unlike filter
and map
, reduce
isn't a built-in; it's in the functools
module. There are plenty of other fun, useful things in that module but we won't be looking at those now. This module isn't included with CircuitPython, but it is with MicroPython. You can find it at https://github.com/micropython/micropython-lib. To use it with CircuitPython, copy micropython-lib/functools/functools.py (either from a clone of the repository or just the single downloaded file) to the CIRCUITPY drive. Once it's there you'll need to import it:
from functools import reduce
reduce
is sometimes confusing at first, but it's actually quite simple. The function you pass it takes two parameters; let's call them accumulator
and x
. This function is called for items of the list (in order), that's the second (x
) argument. The first argument (accumulator
) is the result from the previous time the function was called.
The first time through is a little different. If the optional third argument is supplied to reduce
, it is used as the result of the previous call (as mentioned above). If it's omitted, the first value from the list is used instead.
Some examples should illustrate it:
reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])
this is equivalent to:
((((1+2)+3)+4)+5)
If, instead, we had:
reduce(lambda x, y: x+y, [1, 2, 3, 4, 5], 10)
it would be the same as:
(((((10+1)+2)+3)+4)+5)
We can give it a try and see:
reduce(lambda x,y:x+y,[1,2,3,4,5]) #15 reduce(lambda x,y:x+y,[1,2,3,4,5], 10) #25
Note: the type of the result of reduce
will be the type of the initial value, either the one supplied or the first item in the list.
Of course, like filter
and map
, you don't need a lambda. You can use the name of a function if there's one that does exactly what you want. Since Python lambdas are limited to a single expression, you might sometimes need to write a function just to be used for one of these functions.
How about another example.
def prepend(l, x): l.insert(0, x) return l reduce(prepend, ['a', 'b', 'c'], []) # returns ['c', 'b', 'a']
We can't use a lambda here because insert
doesn't return the list. We need to do that as a second step, which we can't do in a lambda. Also, we need to provide the empty list as the initial accumulator value so that
- the result will be a list, and
- there's something to insert the first item into.
Page last edited March 08, 2024
Text editor powered by tinymce.