Back to Python Examples

Flatten Nested List

Three approaches — recursive, iterative stack, and a Python generator with yield from.

Contents

  1. The Problem
  2. Solution 1 — Recursive
  3. Solution 2 — Iterative Stack
  4. Solution 3 — Generator with yield from
  5. Comparison

1. The Problem

Given an arbitrarily nested list, return a flat list of all integers in order. Nesting can be any depth.

# [1, [2, [3, 4], 5], 6]  →  [1, 2, 3, 4, 5, 6]
# [[1, 2], [3, [4, [5]]]]  →  [1, 2, 3, 4, 5]
# []                        →  []
# [1]                       →  [1]

2. Solution 1 — Recursive

If the item is a list, recurse. If it's an integer, yield it. Clean and readable.

def flatten_recursive(nested):
    result = []
    for item in nested:
        if isinstance(item, list):
            result.extend(flatten_recursive(item))
        else:
            result.append(item)
    return result

print(flatten_recursive([1, [2, [3, 4], 5], 6]))  # [1,2,3,4,5,6]
Time: O(n) where n is total number of elements. Space: O(d) call stack where d is maximum nesting depth. Python's default recursion limit is 1000 — a problem for very deeply nested inputs.

3. Solution 2 — Iterative Stack

Avoid recursion by explicitly managing a stack. Safe for arbitrarily deep nesting.

def flatten_iterative(nested):
    result = []
    stack = [nested]

    while stack:
        current = stack.pop()
        if isinstance(current, list):
            # Push items in reverse so left-to-right order is preserved
            stack.extend(reversed(current))
        else:
            result.append(current)

    return result

print(flatten_iterative([1, [2, [3, 4], 5], 6]))  # [1,2,3,4,5,6]
We push items in reverse order onto the stack so that stack.pop() (LIFO) processes them left-to-right. Without reversed(), the output would come out backwards.

4. Solution 3 — Generator with yield from

The most Pythonic approach. yield from delegates to a sub-generator, naturally handling any depth without building an intermediate list.

def flatten_gen(nested):
    for item in nested:
        if isinstance(item, list):
            yield from flatten_gen(item)
        else:
            yield item

print(list(flatten_gen([1, [2, [3, 4], 5], 6])))  # [1,2,3,4,5,6]

# Works lazily — useful for huge lists:
for num in flatten_gen([1, [2, [3, [4, [5]]]]]):
    print(num, end=' ')  # 1 2 3 4 5
yield from iterable is equivalent to for x in iterable: yield x but is more efficient — it avoids Python's overhead of driving the inner generator loop manually. Available since Python 3.3.

5. Comparison

ApproachTimeSpaceBest for
RecursiveO(n)O(d) call stackClean code, shallow nesting
Iterative stackO(n)O(n) explicit stackVery deep nesting (avoids recursion limit)
GeneratorO(n)O(d) — lazyStreaming / large data — no intermediate list
# Bonus: one-liner using sum() (only works one level deep — don't use for arbitrary nesting)
# sum([[1,2],[3,4],[5]], [])  →  [1, 2, 3, 4, 5]
#
# For arbitrary depth, itertools.chain.from_iterable only flattens one level too.
# The generator approach is always the right answer for arbitrary depth.
In an interview, mention all three and choose the generator — it demonstrates knowledge of Python's iterator protocol, lazy evaluation, and yield from. Then note the recursive approach is O(d) space due to the call stack, so the iterative version is safer for adversarial inputs.