The product function is one of several handy combinatoric iterators included in the itertools module. Combinatoric iterators are related to an area of mathematics called enumerative combinatorics, which is concerned with the number of ways a given pattern can be formed.

product gives us a way to quickly calculate the cartesian product of two or more collections. An example of the cartesian product can be found below:

Image by Quartl, CC BY-SA 3.0

product also frequently serves as an alternative approach to having multiple for clauses inside list comprehensions.

In a previous post, we provided code for a list comprehension that would calculate all the possible roll combinations for two six-sided dice.

roll_combinations = [(d1, d2) for d1 in range(1, 7) for d2 in range(1, 7)]

We can do very much the same thing using the product function.

from itertools import product

dice_combinations = product(range(1, 7), repeat=2)

So what's going on here?

The product function accepts any number of iterables as positional arguments, and has an optional keyword only parameter called repeat.

When we provide two or more iterables as arguments, the product function will find all the ways we can match an element from one of these iterables to an item in every other iterable. For example, we might have a pair of lists like so:

list_1 = ["a", "b", "c"]
list_2 = [1, 2, 3]

When we pass these lists to the product function, we get the following:

cartesian_product = product(list_1, list_2)
# ('a', 1) ('a', 2) ('a', 3) ('b', 1) ('b', 2) ('b', 3) ('c', 1) ('c', 2) ('c', 3)

If we were to add a third iterable, every one of these tuples would be matched up to an item in this third iterable. For example, if we had a third list containing "x", "y", and "z", we would get output like this:

# ('a', 1, 'x') ('a', 1, 'y') ('a', 1, 'z') ('a', 2, 'x') ... etc.

The repeat parameter is most useful for when we want to use the same iterable multiple times. We can see an example of this in our code for finding roll combinations. We can easily add more and more dice by increasing the value of repeat.

If we set a repeat value of 2 or more when we have multiple iterables, product will duplicate all of the iterables for the purposes of finding the cartesian product. The following functions are identical in terms of functionality:

c_product_1 = product(["a", "b", "c"], [1, 2, 3], repeat=2)
c_product_2 = product(["a", "b", "c"], [1, 2, 3], ["a", "b", "c"], [1, 2, 3])

That just about wraps up our introduction to the itertools product function. I hope you learnt something new, and I encourage you to play around with the things we've covered here to really understand how it all works.

We release new snippet posts every Monday, and something a little more substantial on Thursdays, but just in case you forget, you might want to follow us on Twitter to keep up to date with all our content. Next Monday we'll be covering some other cool itertools functions, so make sure to check back next week!

We'd also love to hear about cool tips and tricks you think we should write about, so get in touch!