DocsHub
PythonAdvanced

Flatten Nested List

Learn how to flatten a deeply nested list into a single flat list in Python.

Flatten Nested List

Problem

Given a deeply nested list — a list that contains lists inside lists — flatten it into a single flat list containing all values in order.

Input:  [1, [2, 3], [4, [5, 6]]]
Output: [1, 2, 3, 4, 5, 6]

Input:  [1, [2, [3, [4, [5]]]]]
Output: [1, 2, 3, 4, 5]

Input:  [[1, 2], [3, 4], [5, 6]]
Output: [1, 2, 3, 4, 5, 6]

Input:  [1, 2, 3]
Output: [1, 2, 3]   (already flat — no change)

Input:  [1, [2, [3, [4, [5, [6, [7]]]]]]]
Output: [1, 2, 3, 4, 5, 6, 7]

Logic

Recursive approach:

  1. Loop through each item in the list
  2. If the item is a list — flatten it recursively and add its contents
  3. If the item is not a list — add it directly
  4. Return the flattened result

Stack approach:

  1. Use a stack initialized with the original list
  2. Pop items one at a time
  3. If item is a list — push its contents back onto the stack
  4. If item is not a list — add to result

Flow

Yes No Yes No Start result = empty list Take each item item is a list? Flatten that list recursively Add all its items to result Add item directly to result More items? Return result

Solution 1 — using recursion

The most natural approach. For each item — if it is a list, go deeper. If not, collect it.

def flatten(lst):
    result = []

    for item in lst:
        if isinstance(item, list):
            # item is a list — flatten it and add its contents
            result.extend(flatten(item))
        else:
            # item is a value — add it directly
            result.append(item)

    return result


print(flatten([1, [2, 3], [4, [5, 6]]]))          # [1, 2, 3, 4, 5, 6]
print(flatten([1, [2, [3, [4, [5]]]]]))            # [1, 2, 3, 4, 5]
print(flatten([[1, 2], [3, 4], [5, 6]]))           # [1, 2, 3, 4, 5, 6]
print(flatten([1, 2, 3]))                          # [1, 2, 3]
print(flatten([1, [2, [3, [4, [5, [6, [7]]]]]]]))  # [1, 2, 3, 4, 5, 6, 7]

Code Execution — Solution 1

Trace through flatten([1, [2, [3, [4]]]]):

"flatten([1, [2, [3, [4 item=[2,[3,[4]]]is a list B append 1 "flatten([2, [3, [4 item=2not a list item=[3,[4]]is a list append 2 "flatten([3, [4 item=3not a list item=[4]is a list append 3 "flatten([4 item=4not a list append 4 result = [1,2,3,4]

Step by step call stack:

flatten([1, [2, [3, [4]]]])
  item=1  → not list → append 1
  item=[2,[3,[4]]] → is list → call flatten([2, [3, [4]]])
    item=2  → not list → append 2
    item=[3,[4]] → is list → call flatten([3, [4]])
      item=3 → not list → append 3
      item=[4] → is list → call flatten([4])
        item=4 → not list → append 4
        return [4]
      extend with [4]
      return [3, 4]
    extend with [3, 4]
    return [2, 3, 4]
  extend with [2, 3, 4]
  return [1, 2, 3, 4]

result.extend(flatten(item)) adds all items from the recursive result individually. This is different from result.append(flatten(item)) which would add the entire list as one item — defeating the purpose of flattening.


Solution 2 — using a stack

Avoid recursion entirely. Use a stack to process items iteratively.

def flatten(lst):
    # initialize stack with a copy of the list
    # reversed so first item is processed first when popped
    stack = lst[::-1]
    result = []

    while stack:
        item = stack.pop()   # take from top of stack

        if isinstance(item, list):
            # it is a list — push its contents back onto stack
            # reversed so they come out in the right order
            stack.extend(item[::-1])
        else:
            # it is a value — collect it
            result.append(item)

    return result


print(flatten([1, [2, 3], [4, [5, 6]]]))           # [1, 2, 3, 4, 5, 6]
print(flatten([1, [2, [3, [4, [5]]]]]))             # [1, 2, 3, 4, 5]
print(flatten([[1, 2], [3, 4], [5, 6]]))            # [1, 2, 3, 4, 5, 6]
print(flatten([1, [2, [3, [4, [5, [6, [7]]]]]]]))   # [1, 2, 3, 4, 5, 6, 7]

Code Execution — Solution 2

Trace through flatten([1, [2, 3], 4]):

Initial stack (reversed): [4, [2, 3], 1]

Stepstack.pop()Is list?Actionstackresult
1st1Noappend 1[4, [2, 3]][1]
2nd[2, 3]Yesextend reversed [3, 2][4, 3, 2][1]
3rd2Noappend 2[4, 3][1, 2]
4th3Noappend 3[4][1, 2, 3]
5th4Noappend 4[][1, 2, 3, 4]
Done[][1, 2, 3, 4]

When [2, 3] is popped — we push [3, 2] (reversed) so 2 comes out first next time.

The stack approach avoids Python's recursion limit. For very deeply nested lists — thousands of levels deep — recursion would fail with RecursionError. The stack approach handles any depth since it uses the heap instead of the call stack.


Solution 3 — using a generator

Generate flattened values one at a time using yield. Memory efficient for very large nested structures.

def flatten_generator(lst):
    for item in lst:
        if isinstance(item, list):
            # yield each item from the recursive call
            yield from flatten_generator(item)
        else:
            yield item


# collect all values
print(list(flatten_generator([1, [2, 3], [4, [5, 6]]])))
# [1, 2, 3, 4, 5, 6]

# use directly in a loop — no need to build the whole list
for value in flatten_generator([1, [2, [3, [4, [5]]]]]):
    print(value, end=" ")
# 1 2 3 4 5

Code Execution — Solution 3

Trace through flatten_generator([1, [2, 3]]):

flatten_generator([1, [2, 3]])
  item=1  → not list → yield 1
  item=[2,3] → is list → yield from flatten_generator([2, 3])
    item=2 → yield 2
    item=3 → yield 3

Values yielded in order: 1, 2, 3

yield from delegates to another generator — it yields every value from the sub-generator before continuing.


Solution 4 — one liner using iteration tools

For one level of nesting only — use itertools.chain.from_iterable.

import itertools

def flatten_one_level(lst):
    """Flattens only one level of nesting."""
    return list(itertools.chain.from_iterable(lst))


# one level deep — works perfectly
print(flatten_one_level([[1, 2], [3, 4], [5, 6]]))
# [1, 2, 3, 4, 5, 6]

# more than one level — only first level flattened
print(flatten_one_level([1, [2, [3, 4]], 5]))
# TypeError — 1 and 5 are not iterable


# safer version — handle mixed content
def flatten_one_level_safe(lst):
    result = []
    for item in lst:
        if isinstance(item, list):
            result.extend(item)
        else:
            result.append(item)
    return result


print(flatten_one_level_safe([[1, 2], [3, 4], 5]))
# [1, 2, 3, 4, 5]

Solution 5 — flatten with depth control

Sometimes you only want to flatten N levels deep — not all the way.

def flatten(lst, depth=float("inf")):
    """
    Flatten a nested list.
    depth controls how many levels to flatten.
    depth=1 → one level only
    depth=inf → fully flatten (default)
    """
    result = []

    for item in lst:
        if isinstance(item, list) and depth > 0:
            # go deeper — reduce depth by 1
            result.extend(flatten(item, depth - 1))
        else:
            result.append(item)

    return result


nested = [1, [2, [3, [4, [5]]]]]

print(flatten(nested))           # [1, 2, 3, 4, 5]   — fully flat
print(flatten(nested, depth=1))  # [1, 2, [3, [4, [5]]]]  — one level
print(flatten(nested, depth=2))  # [1, 2, 3, [4, [5]]]    — two levels
print(flatten(nested, depth=3))  # [1, 2, 3, 4, [5]]      — three levels

Code Execution — Solution 5

Trace through flatten([1, [2, [3]]], depth=1):

Stepitemisinstance?depth > 0?Actionresult
1st1Noappend 1[1]
2nd[2, [3]]Yes1 > 0 → Yesflatten([2, [3]], depth=0)

Inside flatten([2, [3]], depth=0):

Stepitemisinstance?depth > 0?Actionresult
1st2Noappend 2[2]
2nd[3]Yes0 > 0 → Noappend [3] as-is[2, [3]]

Back to outer: extend with [2, [3]]

Final: [1, 2, [3]]


Handling other iterables — tuples and sets

A more general version that handles tuples and other iterables, not just lists.

def flatten_any(obj):
    """Flatten lists, tuples, and other iterables."""
    result = []

    for item in obj:
        # check if item is iterable but not a string
        if hasattr(item, "__iter__") and not isinstance(item, str):
            result.extend(flatten_any(item))
        else:
            result.append(item)

    return result


print(flatten_any([1, (2, 3), [4, {5, 6}]]))
# [1, 2, 3, 4, 5, 6] — or [1, 2, 3, 4, 6, 5] (sets are unordered)

print(flatten_any(["hello", ["world", "python"]]))
# ['hello', 'world', 'python'] — strings kept intact

Which solution to use?

SolutionHowBest when
Solution 1RecursionClear, most readable
Solution 2StackVery deep nesting — no recursion limit
Solution 3GeneratorLarge structures — memory efficient
Solution 4itertools.chainOne level of nesting only
Solution 5Depth controlControl how deep to flatten

Output

[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, 6]
[1, 2, 3]
[1, 2, 3, 4, 5, 6, 7]

[1, 2, 3, 4, 5]       depth=inf
[1, 2, [3, [4, [5]]]] depth=1
[1, 2, 3, [4, [5]]]   depth=2
[1, 2, 3, 4, [5]]     depth=3

On this page