Introduction
Python has touched the hearts of many software developers around the world, thanks to its utility and simplicity.
Python provides its users with a number of useful functions and data structures that make working with data easier, including tools used to efficiently loop through data - known as itertools.
This guide will show you how to use Python itertools
to iterate through objects via:
- filter() - The
filter()
function takes in a provided sequence or iterable along with a filtering criteria (a function or lambda). It then tests every element in the sequence to determine if the element fits the filtering criteria, returning only the elements that match that criteria. - islice() - The
islice()
function allows the user to loop through an iterable with astart
andstop
, and returns a generator. - map() - The
map()
function creates an iterable map object that applies a specified transformation to every element in a chosen iterable. - zip() - The
zip()
function takes two iterable objects and returns a tuple of paired elements. The first item in both iterables is paired, the second item in both iterables is paired together, and so on.
We'll start by defining iterable objects and iteration functions and then proceed to look at some examples of the four iteration functions mentioned above.
Note: As of Python 3, filter()
, map()
and zip()
are functionally equivalent to Python 2's itertools
functions ifilter()
, imap()
and izip()
. They all return iterators and don't require imports.
islice()
wasn't ported into the built-in namespace of Python 3. You'll still have to import the itertools
module to use it.
What are Iterable Objects?
An iterable object/iterable can be defined as a container holding data that can be looped/iterated over. Iterable objects in Python include lists, sets, tuples and dictionaries.
Typically, when we work with iterable objects, we loop through them using basic tools like for
loops. We often ignore the features and tools a language may have that can help us with iterative tasks. Iteration tools offer efficient, standardized functions (similar to the functions you'd see in functional programming languages like Haskell) that integrate with other iterative functions to simplify iterative tasks down to just a few lines of code.
The filter() Function
filter()
is a built-in function, and it allows us to take a group of iterable items and test if the elements within the iterable meet your specified filter criteria:
filter(function, iterable)
Since filter()
returns a generator (filter
object) - we'll wrap it in a list()
to convert it back to a simple list. If we were to do filtering via for
and if
statements, it'd look something like:
# Create a simple list numbered 0 to 10
number_list = [x for x in range(0,10)]
# Will filter for even numbers
even_numbers = []
for number in number_list:
if number%2 == 0:
even_numbers.append(number)
print(even_numbers)
This results in:
[0, 2, 4, 6, 8]
By contrast, we could've achieved this same result using filter()
, and passing in the same condition. If the condition is satisfied and True
is returned, it's not filtered out. If the condition isn't satisfied and False
is returned, the element of the iterable is filtered out.
This condition can be supplied either as an anonymous function - lambda
or a standalone function:
number_list = [x for x in range(0,10)]
filtered_list = list(filter(lambda number: number % 2 == 0, number_list))
print(filtered_list)
When provided with a lambda - the number
is an element of the iterable we're currently filtering. For each number
, we check if it's divisible by 2. If so - it's included in the new output:
[0, 2, 4, 6, 8]
As long as the function returns True
or False
, you can extract the function to stand alone and simply reference it here instead of using a lambda
:
number_list = [x for x in range(0,10)]
def is_even(number):
return number%2==0
filtered_list = list(filter(is_even, number_list))
print(filtered_list)
Another function similar to filter()
, called filterfalse()
, can be found in itertools
. This is a counterpart of filter()
that returns the elements that don't satisfy the condition. After importing the function from itertools
we can use our past code and apply filterfalse()
to get just the odd numbers from the list:
from itertools import filterfalse
number_list = [x for x in range(0,10)]
filtered_list = list(filterfalse(lambda number: number % 2 == 0, number_list))
print(filtered_list)
This results in a filtered list of odd numbers:
[1, 3, 5, 7, 9]
Instead of an anonymous function, you can also use a standalone function here too:
from itertools import filterfalse
number_list = [x for x in range(0,10)]
def is_even(number):
return number%2==0
filtered_list = list(filterfalse(is_even, number_list))
print(filtered_list)
The islice() Function
The islice()
function is part of the itertools
library, and it takes an iterable object and returns a segment from it, between the elements defined by the start
and end
arguments given to the function:
itertools.islice(iterable, start, end)
Let's islice()
a string. Since this returns a generator, we'll wrap it in a list to contain the result as well. If you omit the start
argument - the function will slice until the mandatory provided end
argument. If both are provided, it'll slice between them and return that segment:
from itertools import islice
old_string = "I need this, but not this"
print(list(islice(old_string, 11)))
Here, we've sliced the old_string
from its start to the 11th element:
['I', ' ', 'n', 'e', 'e', 'd', ' ', 't', 'h', 'i', 's']
If we provide a start
argument, though, we can slice a specific segment out:
from itertools import islice
old_string = "I need this, but not this"
print(list(islice(old_string, 7, 11)))
Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!
['t', 'h', 'i', 's']
Usually, when working with iterables - we want to end up with an iterable, such as a list. Though slicing is a common operation for strings as well, in which case, we typically want a string - not a list. Thankfully, it's easy to join()
the elements of the list back into a string:
print(''.join(list(islice(old_string, 0, 11))))
Here, we've joined each element to an empty string, which results in the sliced segment being converted into a string:
I need this
The map() Function
The map
function takes an iterable object and a function that applies a transformation to all the items of the iterable:
map(function, iterable)
The map()
function is included in Python's built-in functions, so there's no need to import anything. map()
offers the exact same functionality as imap()
from the itertools
module in Python 2.
Generally speaking - it's very useful when you'd like to perform batch-transformations on each element of an iterable. Each element maps to a transformed version of that element, or a result of another operation performed by or on that element.
Say, you'd like to raise every integer element to the power of 2:
number_list = [x for x in range(0,10)]
numbers_powered = []
for number in number_list:
numbers_powered.append(number**2)
print(numbers_powered)
This results in a sequence of:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Now, we can simplify this with a map()
:
print(list(map(lambda x: x**2, number_list)))
For each element in the number_list
iterable - the element is raised to the power of two, and put in a new list:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Of course, instead of an anonymous function, you can also define other functions instead:
number_list = [x for x in range(0,10)]
def function(number):
print("Performing transformation on number ", number)
return number**2
print('Original list: ', number_list)
mapped_list = list(map(function, number_list))
print('Transformed list: ', mapped_list)
This results in:
Original list: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Performing transformation on number 0
Performing transformation on number 1
Performing transformation on number 2
Performing transformation on number 3
Performing transformation on number 4
Performing transformation on number 5
Performing transformation on number 6
Performing transformation on number 7
Performing transformation on number 8
Performing transformation on number 9
Transformed list: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
The zip() Function
The zip()
function accepts 0..n
iterable objects and creates 0..n
tuples containing the nth element of each of these iterables:
zip(iterable_1, iterable_2, iterable_3...)
It's a built-in function since Python 3 and offers the same functionality izip()
from the itertools
module offered in Python 2.
Let's zip()
together a list of names and a list of IDs, where the first name is zipped with the first ID, the second name is zipped with the second ID, et cetera:
names_list = ['Francis', 'Drake', 'Alexander', 'Robert', 'Elon']
id_list = ['001', '002', '003', '004', '005']
print(list(zip(names_list,id_list)))
This results in:
[('Francis', '001'), ('Drake', '002'), ('Alexander', '003'), ('Robert', '004'), ('Elon', '005')]
Note: If these iterables are not of the same shape, such as names_list
having 5 elements, and id_list
having 10 elements, only the first 5 would be mapped, while the rest of id_list
would be ignored. The longest common sequence will be mapped.
As usual, this returns a generator - so we've wrapped it in a list()
.
The same functionality and behavior is present with more than two iterables - you can provide an unbounded number of them, in fact:
names_list = ['Francis', 'Drake', 'Alexander', 'Robert', 'Elon']
last_name_list = ['Brown', 'Johnson', 'Tiedemann', 'Mann']
id_list = ['001', '002', '003', '004', '005']
zipped_list = list(zip(names_list, last_name_list, id_list))
print(zipped_list)
[('Francis', 'Brown', '001'), ('Drake', 'Johnson', '002'), ('Alexander', 'Tiedemann', '003'), ('Robert', 'Mann', '004')]
Since the names_list
is of length 5, while the other two iterables are of length 4 - the last element of names_list
doesn't have a pair.
This is a great tool to group related items that appear in different contexts.
Conclusion
Python comes with a number of built-in functions that help engineers easily and efficiently manipulate data, through a high-level API. Iteration is a very common operation - and Python's iteration tools are very handy for one-line, functional-style operations on elements.
In this guide, we've taken a look at the filter()
, map()
, islice()
and zip()
functions.
Although islice()
resides in the itertools
module, and isn't present in the built-in namespace - it's a type of function you'll commonly use for sub-sequencing other sequences, and is commonly used with the other functions highlighted in the guide.