Python uses None, not nil or null, to represent the absence of a value, and the reliable check is value is None.
The syntax is simple. The design decision is not. Production bugs appear when missing, empty, zero, false, invalid, and not-yet-loaded are squeezed into one vague falsy bucket.
Table of contents
- None is a singleton with its own type
- Missing, empty, zero, and false are different states
- Use explicit sentinels when None is valid data
- Handle None at database and API boundaries
- Make absence observable without making it noisy
- How this fits the rest of the stack
- FAQ
None is a singleton with its own type
None is the single instance of NoneType. Functions without an explicit return value return it automatically. It can also be an intentional placeholder, a default argument, or a signal that a lookup found no value.
result = None
if result is None:
print('No result yet')
print(type(result)) # NoneType
Use is None and is not None. Identity expresses the singleton check directly and cannot be surprised by a class that overloads equality. Style guides recommend it because the code says exactly what it means.
Missing, empty, zero, and false are different states
None, '', 0, False, and empty containers are all falsy, but they often carry different business meaning. A zero balance exists. An empty list may be a completed query with no matches. None may mean the query never ran or the field was omitted.
def describe_limit(limit):
if limit is None:
return 'use the account default'
if limit == 0:
return 'disable processing'
return f'process up to {limit}'
Write a truthiness check only when all falsy values genuinely share behavior. Otherwise compare the state you intend. Short code is not automatically clear code.
Use explicit sentinels when None is valid data
Sometimes None is a legitimate value and you still need to distinguish it from an omitted argument. Create a unique sentinel object. Frameworks use this pattern for configuration, patch requests, caches, and lazy loading.
MISSING = object()
def update_email(value=MISSING):
if value is MISSING:
return 'leave unchanged'
if value is None:
return 'clear email'
return f'set email to {value}'
A named sentinel turns an implicit convention into a state the code can test. For public APIs, document it through types and request schemas rather than leaking a private object across boundaries.
Handle None at database and API boundaries
SQL NULL, JSON null, and Python None often map to one another, but their surrounding semantics differ. PATCH requests may distinguish an omitted field from an explicit null. Database constraints may allow null but reject empty strings. Serialization libraries may omit None fields or emit them depending on configuration.
Validate at the boundary and keep the internal model intentional. Optional type hints such as str | None communicate that absence is allowed, but they do not validate runtime input or explain what absence means to the product.
Make absence observable without making it noisy
Do not catch every AttributeError and blame None. Validate critical inputs near entry points, fail with context, and log identifiers rather than secrets. A stack trace that says an object has no attribute is less useful than an error explaining which upstream field was missing.
Defaults should be deliberate too. Replacing every None with a convenient value can hide upstream data loss. Decide where a default is safe, where absence should propagate, and where the request must fail.
How this fits the rest of the stack
If the application behind that data model needs an API, database, storage, and background work, put the line items beside each other in the RunxBuild hosting calculator. The RunxBuild dashboard is there when the model is ready to become a deployed service.
Useful related references:
- Python Not Equal: != vs is not, and Why the Difference Bites
- Python Integer Division: Why // Floors and Why -7 // 2 Is -4
- Python for Websites: Where It Fits and Where It Does Not
- Python services on RunxBuild
FAQ
What is nil in Python?
Python does not have a nil keyword. The equivalent concept is represented by the singleton value None.
Should I use == None or is None?
Use is None. It checks identity with the singleton and avoids custom equality behavior.
Is None the same as an empty string?
No. Both are falsy, but an empty string is a present string with zero characters while None represents absence.
What type is None?
Its type is NoneType. Modern type hints commonly express an optional string as str | None.
Is None the same as NaN?
No. None represents absence as a Python object. NaN is a special floating-point value representing an undefined or unrepresentable numeric result.