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:
- Loop through each item in the list
- If the item is a list — flatten it recursively and add its contents
- If the item is not a list — add it directly
- Return the flattened result
Stack approach:
- Use a stack initialized with the original list
- Pop items one at a time
- If item is a list — push its contents back onto the stack
- If item is not a list — add to result
Flow
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]]]]):
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]
| Step | stack.pop() | Is list? | Action | stack | result |
|---|---|---|---|---|---|
| 1st | 1 | No | append 1 | [4, [2, 3]] | [1] |
| 2nd | [2, 3] | Yes | extend reversed [3, 2] | [4, 3, 2] | [1] |
| 3rd | 2 | No | append 2 | [4, 3] | [1, 2] |
| 4th | 3 | No | append 3 | [4] | [1, 2, 3] |
| 5th | 4 | No | append 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 5Code 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 3Values 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 levelsCode Execution — Solution 5
Trace through flatten([1, [2, [3]]], depth=1):
| Step | item | isinstance? | depth > 0? | Action | result |
|---|---|---|---|---|---|
| 1st | 1 | No | — | append 1 | [1] |
| 2nd | [2, [3]] | Yes | 1 > 0 → Yes | flatten([2, [3]], depth=0) |
Inside flatten([2, [3]], depth=0):
| Step | item | isinstance? | depth > 0? | Action | result |
|---|---|---|---|---|---|
| 1st | 2 | No | — | append 2 | [2] |
| 2nd | [3] | Yes | 0 > 0 → No | append [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 intactWhich solution to use?
| Solution | How | Best when |
|---|---|---|
| Solution 1 | Recursion | Clear, most readable |
| Solution 2 | Stack | Very deep nesting — no recursion limit |
| Solution 3 | Generator | Large structures — memory efficient |
| Solution 4 | itertools.chain | One level of nesting only |
| Solution 5 | Depth control | Control 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