-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
83: Practice Exercise: Recursive Problem Solving Drills
By now, you've got the theory of recursion down: a base case to stop the bleeding and a recursive step to move toward that base. But there's a huge gap between understanding that concept and actually staring at a blank screen trying to figure out how to break a problem down. To bridge that gap, I want to walk you through how I'd approach a common real-world headache: flattening a deeply nested list.
Dealing with the "Nested List" Nightmare
Imagine you're scraping data or parsing a weird JSON file, and you end up with a list like this: [1, [2, [3, 4], 5], 6, [7, 8]]. You just want a simple, flat list of integers. You could try a for loop, but you don't know how deep the nesting goes. This is where recursion shines because the function doesn't need to know the depth; it just needs to know how to handle one level at a time.
Mapping out the Recursive Path
My mental model here is simple: I'll iterate through the list. If I hit an element that is itself a list, I'll tell the function to "go inside" that list and do the same thing. If I hit something that isn't a list, I've hit my base case—it's just a value I can collect.
def flatten_list(nested_data):
flat = []
for item in nested_data:
if isinstance(item, list):
# This is the recursive call
flat.append(flatten_list(item))
else:
flat.append(item)
return flat
Where I messed up the aggregation
I'll be honest—I've written the snippet above more times than I'd like to admit before realizing it's wrong. If you run that code with [1, [2, 3]], you won't get [1, 2, 3]. You'll get [1, [2, 3]]. Wait, that's exactly what we started with!
Here is the mistake: I used .append() on the result of the recursive call. Since flatten_list(item) returns a list, .append() just puts that entire list inside my flat list as a single element. I'm essentially just rebuilding the nesting I was trying to destroy. I forgot that the result of a recursive call on a list is still a list.
Cleaning up the final logic
To fix this, I need to merge the returned list into my current list rather than nesting it. In Python, .extend() is the tool for this. It takes the elements out of the provided list and adds them individually.
def flatten_list(nested_data):
flat = []
for item in nested_data:
if isinstance(item, list):
# Use extend instead of append to merge the results
flat.extend(flatten_list(item))
else:
flat.append(item)
return flat
# Testing it out
nested = [1, [2, [3, 4], 5], 6, [7, 8]]
print(flatten_list(nested)) # Output: [1, 2, 3, 4, 5, 6, 7, 8]
Now the logic holds. The function dives as deep as necessary, and as it "bubbles back up" the recursion stack, it carries those values back and extends them into the higher-level lists. It's a clean, elegant way to handle unpredictable depth without writing twenty nested while loops.
📋 Practical Task
Exercise: Deep-Cleaning a Nested Directory Structure
In this exercise, you'll apply the same recursive logic we used for lists to a dictionary. This simulates how a file system works (folders containing files or other folders).
You are given a dictionary representing a folder structure. Some keys map to strings (files), and others map to dictionaries (sub-folders). Your task is to write a recursive function called list_all_files(folder_structure) that returns a single flat list of all the file names found at any depth.
Starting Data:
folder_structure = {
"Documents": {
"Resume.pdf": "file",
"Work": {
"Project_A.docx": "file",
"Project_B.docx": "file",
"Archive": {
"Old_Notes.txt": "file"
}
},
"Budget.xlsx": "file"
},
"Photos": {
"Vacation.jpg": "file",
"Family": {
"Mom.png": "file",
"Dad.png": "file"
}
},
"todo.txt": "file"
}
Requirements:
- Your function must handle an arbitrary number of nested folders.
- The final output should be a flat list of strings:
['Resume.pdf', 'Project_A.docx', 'Project_B.docx', 'Old_Notes.txt', 'Budget.xlsx', 'Vacation.jpg', 'Mom.png', 'Dad.png', 'todo.txt'](Order does not matter). - Avoid using any external libraries; stick to standard dictionary iteration.
There are no comments for now.