itertools.product() With No Arguments Returns One Result (tested)

Generic code that builds a cartesian product out of a variable number of option lists -- for a test matrix, a CI build matrix, or request parameter combinations -- often calls itertools.product(*option_lists). We measured what happens at the edges, when one list is empty versus when there are no lists at all, and the two zero-ish cases behave completely differently.

1. One empty option list makes the whole product empty

import itertools

def build_combinations(option_lists):
    return list(itertools.product(*option_lists))

print(build_combinations([["a", "b"], ["x", "y"]]))
print(build_combinations([["a", "b"], []]))  # one dimension has zero choices
[('a', 'x'), ('a', 'y'), ('b', 'x'), ('b', 'y')]
[]

Measured: as soon as any one of the lists passed to product() is empty, the entire result is an empty list -- zero combinations, even though the other list still has two real choices in it. This matches how a cartesian product works mathematically: if one dimension has no values, there's nothing to combine them with.

2. Passing no lists at all gives one result, not zero

import itertools

def build_combinations(option_lists):
    return list(itertools.product(*option_lists))

print(build_combinations([]))  # zero dimensions, not one empty dimension
[()]

Measured: this returns a list containing one tuple -- an empty tuple -- not an empty list. product() with zero iterables has exactly one way to pick "nothing from nothing": the empty combination. That's a different case from "one dimension that happens to have zero choices," even though both might look like "there's nothing to combine" at a glance.

3. repeat=0 behaves the same way as zero arguments

import itertools

print(list(itertools.product([1, 2, 3], repeat=0)))
[()]

Measured: repeat=0 means "use zero copies of this iterable," which is equivalent to passing no iterables at all -- it also returns [()], one empty-tuple result, not an empty list.

4. Why this matters: a generic combination-builder can silently produce one bogus "test case"

import itertools

def build_combinations(option_lists):
    results = list(itertools.product(*option_lists))
    return results

# Caller expects: "no option lists configured" -> "no test cases to run"
test_cases = build_combinations([])
print(f"generated {len(test_cases)} test case(s):", test_cases)
if test_cases:
    print("running at least one test case, with zero parameters each")
generated 1 test case(s): [()]
running at least one test case, with zero parameters each

Measured: code that treats len(test_cases) == 0 as the signal for "nothing configured, skip this step" will not skip -- it gets exactly one test case with no parameters, and runs it. If the real intent is "zero configured dimensions means zero cases," check the length of option_lists explicitly before calling product(), rather than inferring intent from the length of its output.

Frequently asked questions

Does itertools.product() with an empty list always return an empty result?

Not always -- we measured two different cases. An empty list nested among other option lists does make the whole product empty ([]). But calling product() with no arguments at all, or with repeat=0, returns one result: a single empty tuple, [()].

Why does itertools.product() with no arguments return [()] instead of []?

Because product() with zero iterables has exactly one valid combination -- picking nothing from nothing. That's mathematically different from a dimension that has values but zero of them, which is what makes the whole product empty.

How do I make a combination-builder return zero cases when no option lists are configured?

Check len(option_lists) == 0 yourself before calling itertools.product(), rather than checking len() of the product's output -- the output being length 1 doesn't mean 'something was configured.'

Is itertools.product(x, repeat=0) the same as itertools.product() with no arguments?

We measured them producing the identical result, [()] -- repeat=0 means zero copies of the given iterable, which collapses to the same zero-dimensions case as passing nothing at all.

Does this affect itertools.combinations or permutations too?

Not measured here -- this guide is specifically about product()'s empty-vs-zero-arguments distinction. combinations() and permutations() have their own, different edge behavior for r larger than the input length.

Written with AI assistance. Measured on 2026-10-04 with Python 3.13.5's built-in itertools module: ran itertools.product() with one of several lists empty, with zero lists passed at all (product() with no arguments), and with repeat=0, comparing the resulting number of tuples in each case. PlainHeaders is our extension; Python's itertools module is built in.