Never Use None for Missing Values Again (Do This Instead)

There’s a better, safer way to represent missing data in Python — here’s what you should use instead.

Never Use None for Missing Values Again (Do This Instead)
Photo by Hassan Pasha on Unsplash

Using None feels simple — until it silently breaks your logic.

Never Use None for Missing Values Again (Do This Instead)

Let’s be honest.
We’ve all done this:

def get_user_email(user): 
    return user.get("email", None)

Seems harmless, right?

But if you’ve worked on a growing codebase or touched production data pipelines, you’ve likely seen how using None for missing values can quietly snowball into a tangled mess of bugs, unreadable conditionals, and unclear intent.

It’s time to stop reaching for None as your default missing value.

Here’s why — and what you should do instead.

The Problem with None

In Python, None is meant to represent the absence of a value. That’s fine, but it’s also incredibly generic and ambiguous.

Is it missing?

if value is None: 
    # Maybe missing?

Or maybe it’s not set yet?

def __init__(self): 
    self.cache = None  # Will be filled later

Or maybe it’s intentional?

def send_email(to=None): 
    if to is None: 
        to = "default@example.com"

This overloading of None creates semantic confusion. As a result, you often find yourself writing conditionals that are defensive, unclear, or just plain wrong.

Use Sentinel Objects Instead

When you need to represent missing or placeholder values, don’t use None.
Use a custom sentinel object.

MISSING = object()

Now, you can clearly check:

def get_user_email(user): 
    email = user.get("email", MISSING) 
    if email is MISSING: 
        # handle missing case 
        ...

You’ve just made the intent crystal clear:
You’re not checking for None, you’re checking if the key was ever there.

Why Sentinel Objects Rock

1. Explicit is better than implicit

Using a unique object as your sentinel value is Pythonic. It removes ambiguity and forces you (and your teammates) to think intentionally about what’s missing, why it’s missing, and how to handle it.

2. Avoids false positives

Unlike None, a sentinel won’t collide with other "falsy" values like 0, '', or False.

if value is not MISSING: 
    do_something(value)

Clean. Precise. No surprises.

3. Easier to debug

When debugging or logging, you instantly know if a value is the sentinel. It’s unmistakable — because it’s yours.

How to Implement Sentinel Values Cleanly

Here’s a reusable pattern:

class _Missing: 
    def __repr__(self): 
        return "<MISSING>" 
 
MISSING = _Missing()

You can even make it fancier:

class Sentinel: 
    def __init__(self, name): 
        self.name = name 
 
    def __repr__(self): 
        return f"<{self.name}>" 
 
MISSING = Sentinel("MISSING") 
UNSET = Sentinel("UNSET")

Now you have descriptive, unique placeholders for different types of absence.

Bonus: Use Ellipsis as a Built-in Sentinel

Python has a built-in singleton called Ellipsis, represented by ....

def do_something(config=...): 
    if config is ...: 
        raise ValueError("config is required")

This is a clever hack for certain situations — but it can be confusing unless you’re consistent about using it. Still, it’s better than overloading None.

Real-World Use Cases

Optional Arguments in Functions

Instead of:

def connect(db=None): 
    db = db or get_default_db()

Use:

def connect(db=MISSING): 
    if db is MISSING: 
        db = get_default_db()

Now, you respect None if someone explicitly passed it.

Data Validation

For APIs or form inputs, sentinel values can differentiate between:

  • A value that’s missing entirely
  • A value explicitly set to null / None

Which often carry different meanings.


Wrapping Up

Using None everywhere may feel convenient, but it’s rarely the right tool for nuanced cases.

If your code needs to distinguish between:

  • Not present
  • Intentionally empty
  • Explicitly null

Then None won’t cut it.

Define your own sentinel objects

Be explicit about missing values

Clean up your conditionals


Pro Tip:

Sentinel objects are one of those advanced Python techniques that feel small but have a big impact on code clarity, especially in large systems or libraries.

So next time you’re tempted to write or None

👀 Think again.


If you enjoyed this, follow me for more Python best practices and dev insights.
Let’s write better code — together.

Photo by Amr Taha™ on Unsplash