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:

  1. A base case — the condition where it stops calling itself (no base case = infinite recursion → RecursionError)
  2. 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 or os.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.