Python Tip of the Week: Using Dispatch Tables for Cleaner Validation

Python Tip of the Week logo: Python Post-it note on monitor
Tips and techniques to improve your Python skills

Let’s be honest: argument validation code is rarely the proudest part of anyone’s repo.

Most of us start with the usual suspects:

❌ The dreaded inverted-V tower of if/else statements
❌ A graveyard of guard clauses scattered line after line


Using a dispatch table for validation rules means: one dictionary, one loop, infinite sanity.


Both work fine… until they don’t. Then you’re left maintaining a wall of conditionals that feels like it was designed by a committee of goblins.

There’s a better way: dispatch tables!


The Inverted-V Approach 😱

Here’s how many programmers begin - especially those with a C++/C#/Java background. You just want to validate a few things… but suddenly your code resembles a Cyrano de Bergerac profile:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
def create_user(username: str, age: int, email: str) -> None:
    if username:
        if len(username) >= 3:
            if age is not None:
                if age >= 18:
                    if email:
                        if '@' in email:
                            # All validations passed - create the user
                            print(f"Creating user: {username}")
                            return
                        else:
                            raise ValueError("Email must contain '@'")
                    else:
                        raise ValueError("Email is required")
                else:
                    raise ValueError("Age must be 18 or older")
            else:
                raise ValueError("Age is required")
        else:
            raise ValueError("Username must be at least 3 characters")
    else:
        raise ValueError("Username is required")

Takeaway: Sure, it works… but it’s awkward, fragile, and painful to extend. Add one more rule and the nesting level reaches the earth’s mantle.

The Guard Clause Approach 😬

Next step: fail fast. One check per line. No nesting. Looks like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
def create_user(username: str, age: int, email: str) -> None:
    if not username:
        raise ValueError("Username is required")
    
    if len(username) < 3:
        raise ValueError("Username must be at least 3 characters")
    
    if age is None:
        raise ValueError("Age is required")
    
    if age < 18:
        raise ValueError("Age must be 18 or older")
    
    if not email:
        raise ValueError("Email is required")
    
    if '@' not in email:
        raise ValueError("Email must contain '@'")
    
    # All validations passed - create the user
    print(f"Creating user: {username}")

Takeaway: Cleaner than the inverted-V, but now every new rule means another line. Add 10 rules, and you’re back to scrolling forever.

Enter Dispatch Tables ✨

A dispatch table is just a dictionary where the key is the error message and the value is a check function (usually a lambda). Instead of scattering control flow everywhere, you centralize the rules in one tidy structure.

Here’s a snack-sized example you can paste right now:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
def create_user(username: str, age: int, email: str) -> None:
    rules = {
        "Username is required": lambda: bool(username),
        "Username must be at least 3 characters":
            lambda: len(username or '') >= 3,
        "Age is required": lambda: age is not None,
        "Age must be 18 or older": lambda: age is None or age >= 18,
        "Email is required": lambda: bool(email),
        "Email must contain '@'": lambda: not email or '@' in email,
    }
    
    errors = [msg for msg, check in rules.items() if not check()]
    if errors:
        raise ValueError("Validation failed:\n" + "\n".join(errors))
    
    # All validations passed - create the user
    print(f"Creating user: {username}")

Takeaway:

  • All rules live in one place (in the dictionary).

  • Adding a rule requires simply adding a dictionary entry - either a lamda or a function name that returns a boolean indicating validation success or failure.

  • No if sprawl, which leads to loss of readability. The result is obviously higher maintenace costs.

Final Thoughts💡

Dispatch tables turn “ugh, validation code” into something declarative, compact, and kind of fun to work with. They are:

  • Readable: Each rule is self-explanatory, almost like documentation.

  • Maintainable: You can add or remove rules without touching control flow.

  • Consistent: Whether you have 3 rules or 30, the same single loop handles them all.

Therefore, the next time you’re staring at a jungle of if/else checks or guard clauses, stop. Reach for a dispatch table instead. Your future self (and your teammates) will thank you.

Resources 📚

I’ve gathered some trustworthy references that provide more detail if you want to read more:

Python dict documentation

Wikipedia: Dispatch table

Knuth on structured programming