Don’t Use Recursion in Python Anymore — Here’s Why (And What to Do Instead)
Python’s recursion isn’t what you think.

You’ve been solving problems the hard way — and Python’s not happy about it.
Don’t Use Recursion in Python Anymore — Here’s Why (And What to Do Instead)
You’ve probably heard this before: Recursion is elegant. Recursion is powerful.
And in many languages, it absolutely is.
But in Python? Recursion can be a trap.
Sure, it’s a go-to tool when learning algorithms. It feels mathematically beautiful. You write a function that calls itself, and voila — it looks like you know what you’re doing.
But if you’ve ever hit a RecursionError: maximum recursion depth exceeded
, you’ve already felt the edge of the sword.
Python isn’t optimized for deep recursion. And once you start building real-world applications — anything beyond toy problems — you’ll quickly realize that recursion is more liability than magic.
Why Recursion Falls Flat in Python
Let’s get right to it: Python has a recursion limit.
By default, that’s 1,000 function calls deep.
This means if your recursive function goes too far — say, while processing a large tree or doing a depth-first search on a huge graph — your program crashes. Hard.
Here’s what happens under the hood:
- Each function call in Python consumes stack space.
- Python does not perform tail call optimization (a compiler optimization that languages like Scheme or Scala use to reuse stack frames).
- As a result, deeply recursive calls quickly blow up the call stack.
Let’s illustrate that with an innocent-looking function:
def factorial(n):
if n == 0:
return 1
return n * factorial(n - 1)
print(factorial(5000))
Run this, and you’ll get:
RecursionError: maximum recursion depth exceeded in comparison
That’s your elegant recursion collapsing on itself.
But Recursion Is Clean, Right?
Sure, recursion can make code look clean — for small problems:
def fib(n):
if n <= 1:
return n
return fib(n - 1) + fib(n - 2)
It’s short. It’s readable. It’s also horribly inefficient.
This implementation is exponential in time complexity (O(2^n)). It recalculates the same values again and again.
Try calling fib(40)
, and your laptop fans will start to sing.
Iteration Is Boring — But It Works
You don’t need recursion to solve recursive problems. You need a stack.
And Python lists make great stacks.
Here’s how to rewrite factorial iteratively:
def factorial_iterative(n):
result = 1
for i in range(2, n + 1):
result *= i
return result
Much faster. No risk of a recursion crash. Just good old for-loops.
And the Fibonacci example?
def fib_iterative(n):
if n <= 1:
return n
prev, curr = 0, 1
for _ in range(2, n + 1):
prev, curr = curr, prev + curr
return curr
Runs instantly, even for large n
.
The Hidden Cost of Recursion in Python
If performance isn’t enough to sway you, consider these other downsides:
1. Harder to Debug
Recursive call stacks are difficult to follow in real-time. You lose track of where you are — and what variables hold.
2. Memory Overhead
Every recursive call creates a new stack frame. That’s a lot of memory wasted when iteration could do the same with one frame.
3. Lack of Tail Call Optimization
Many functional languages automatically optimize tail-recursive calls into loops. Python doesn’t — by design. Guido van Rossum (Python’s creator) deliberately left it out to keep debugging simple and explicit.
“If you want to write efficient code, use iteration.” — Python community wisdom
So When Can You Use Recursion?
To be fair, recursion isn’t always evil in Python. It’s acceptable when:
- The depth of recursion is guaranteed to be shallow
- You’re writing code for educational purposes
- You’re working with functional-style problems (e.g., quicksort on small arrays)
Or when recursion actually clarifies the logic better than iteration — and performance isn’t a concern.
But for production? Recursion is often a ticking time bomb.
Practical Tip: Simulate Recursion With an Explicit Stack
Let’s say you need to traverse a binary tree. Recursion is natural here — but you can do it iteratively with an explicit stack.
def inorder_traversal(root):
stack = []
current = root
result = []
while stack or current:
while current:
stack.append(current)
current = current.left
current = stack.pop()
result.append(current.val)
current = current.right
return result
Cleaner, safer, and infinitely scalable.
Real-World Case Study: DFS in a Graph
Here’s a quick example from a web scraping script I wrote.
I used DFS to traverse pages and avoid cycles. The recursive version kept hitting limits. The fix?
def dfs_iterative(graph, start):
visited = set()
stack = [start]
while stack:
node = stack.pop()
if node not in visited:
visited.add(node)
stack.extend(graph[node] - visited)
return visited
Boom. Scales to thousands of nodes. No crash. No problem.
Conclusion: Stop Using Recursion as a Default
Python isn’t designed for heavy recursion. If you’re reaching for recursion as your go-to pattern, stop and ask:
Can this be done with iteration?
Chances are, yes. And it’ll be faster, safer, and easier to maintain.
So the next time someone shows off a fancy recursive solution, impress them with your iterative, robust, Pythonic alternative.
Recursion is like a beautiful sports car — it looks great but crashes easily. Iteration is your dependable all-terrain vehicle. For Python, go off-road.
