Writing Python Functions and Recursion Basics
Defining a simple function
A function packages reusable logic under a name. You define it with def, give it parameters, and optionally return a value:
def hash_file(path):
"""Compute the SHA-256 hash of a file."""
import hashlib
h = hashlib.sha256()
with open(path, "rb") as f:
for chunk in iter(lambda: f.read(4096), b""):
h.update(chunk)
return h.hexdigest()
result = hash_file(r"C:\evidence\image.dd")
print(result)
Key parts:
- def keyword, function name, parameters in parentheses
- Docstring (the triple-quoted string) — document what it does and returns
- return sends a value back to the caller; without it the function returns None
A recursive function
Recursion is when a function calls itself to solve smaller pieces of a problem. It needs two things:
- A base case — the condition where it stops calling itself (no base case = infinite recursion →
RecursionError) - A recursive case — where the function calls itself with a smaller/simpler input
Example: walk a directory tree and collect all file paths (a common forensics task):
import os
def collect_files(root):
"""Recursively collect all file paths under root."""
files = []
for entry in os.listdir(root):
full = os.path.join(root, entry)
if os.path.isdir(full):
files.extend(collect_files(full)) # recursive case
else:
files.append(full)
return files
all_files = collect_files(r"C:\Cases\2024-001")
print(len(all_files), "files found")
Tracing collect_files("C:\Cases"):
- Lists contents; for each directory found, calls itself on that subdirectory
- Eventually reaches directories with no subdirectories — the loop simply adds files and returns (implicit base case)
Recursion rules of thumb
- Always identify the base case before writing the recursive call
- Each recursive call must make progress toward the base case
- Python's default recursion limit is ~1000 (see
sys.getrecursionlimit()); deep trees may exceed it — that's when iteration oros.walk()is safer - Anything recursive can be written iteratively; recursion trades extra memory (call stack) for often-simpler code
Note: for real directory walking, os.walk() already does this iteratively under the hood — recursion here is for learning the pattern.