Python None represents the absence of a value. It is not zero, an empty string, an empty list, or False. It is a unique singleton object used when a result is missing, a function has no explicit return value, an optional parameter was not provided, or an object has not been initialized yet.
Understanding None prevents common bugs such as calling methods on a missing value, confusing a valid zero with missing data, or using an incorrect comparison. This guide covers identity checks, truthiness, function returns, optional values, default arguments, sentinels, dictionaries, type hints, Pandas, JSON, and practical validation.
Related topics include Python booleans, return statements, and data types.
What Is None?
result = None
print(result)
print(type(result))The type is NoneType. A normal Python process has one None object, so identity comparison is the correct way to check it.
The official Python constants documentation defines None. The PEP 8 programming recommendations explain comparison style.
Use is None
value = None
if value is None:
print("No value is available")For the opposite condition:
if value is not None:
print("A value is available")Avoid value == None. Equality can be customized by a class, while is checks whether both references point to the same singleton object. The == versus is guide explains value and identity comparisons.
None Is Falsy, but It Is Not False
print(bool(None))
print(None is False)bool(None) is false, but None and False are different objects with different meanings.
This condition accepts several falsy values:
if not value:
print("The value is falsy")It runs for None, zero, empty strings, empty lists, and other falsy values. Use is None when only missingness should match.
None vs Zero
stock_count = 0
unknown_stock = None
print(stock_count is None)
print(unknown_stock is None)Zero is valid numerical information. None says the information is unavailable or not assigned. Treating both as equivalent can create incorrect reports and decisions.
None vs Empty String
middle_name = ""
uncollected_name = NoneAn empty string may mean the field was intentionally left blank. None may mean the field was never collected. The distinction depends on the data model, but it should be defined consistently.
Functions Return None Implicitly
A function without an explicit return statement returns None:
def show_message() -> None:
print("Hello")
result = show_message()
print(result)The annotation -> None communicates that the function performs an action rather than returning a useful value.
A Bare return Also Returns None
def print_positive(number: int) -> None:
if number <= 0:
return
print(number)The bare return exits early and its result is None.
Do Not Assign the Result of In-Place Methods
Many mutating methods return None to emphasize that they change the object:
numbers = [3, 1, 2]
result = numbers.sort()
print(numbers)
print(result)numbers.sort() modifies the list and returns None. Use sorted(numbers) when you need a new sorted list. See sort versus sorted.
Return None for a Missing Result
def find_user(users: list[dict], user_id: int) -> dict | None:
for user in users:
if user["id"] == user_id:
return user
return NoneThe caller must check the result:
user = find_user(users, 42)
if user is None:
print("User not found")
else:
print(user["name"])When absence is a normal possibility, returning None can be clear. When absence is exceptional, raising an exception may be better.
Type Hints for Optional Values
Modern annotations use a union with None:
def load_name(user_id: int) -> str | None:
...Older compatible code may use Optional:
from typing import Optional
def load_name(user_id: int) -> Optional[str]:
...The annotation tells editors and type checkers that callers must handle both a string and None. Read the type hints guide for more examples.
Use None as a Default Argument
None is commonly used to avoid mutable default arguments:
def add_item(item: str, items: list[str] | None = None) -> list[str]:
if items is None:
items = []
items.append(item)
return itemsEach call without items creates a new list. This avoids sharing one default list across calls.
When None Is a Valid Argument
Sometimes None itself is meaningful and cannot distinguish “not provided” from “provided as None.” Use a unique sentinel:
_MISSING = object()
def update_profile(name=_MISSING):
if name is _MISSING:
print("Keep the current name")
elif name is None:
print("Remove the name")
else:
print(f"Set the name to {name}")A private sentinel makes all three states explicit.
None in Dictionaries
user = {
"name": "Taylor",
"phone": None,
}
if user["phone"] is None:
print("Phone number not provided")Be careful with dict.get():
phone = user.get("phone")The result is None both when the key is absent and when its stored value is None. Use membership when the distinction matters:
if "phone" not in user:
print("The field is missing")
elif user["phone"] is None:
print("The field exists but has no value")None in Lists
measurements = [12.5, None, 14.0, None, 13.2]
valid_measurements = [
value for value in measurements if value is not None
]
print(valid_measurements)Do not use if value when zero is a valid measurement, because it would remove both zero and None.
JSON null Becomes None
import json
payload = '{"name": "Alex", "phone": null}'
data = json.loads(payload)
print(data["phone"] is None)When Python data is encoded as JSON, None becomes null. Validate API contracts because some services distinguish a missing key from a key containing null.
Database NULL and None
Database drivers commonly map SQL NULL to Python None. The exact query and schema rules still matter. A nullable database column should normally correspond to an optional value in application code.
Pandas Missing Values
Pandas may represent missing data with NaN, NaT, None, or nullable extension values depending on the column type. Do not rely only on is None for a DataFrame column. Use Pandas missing-value tools:
import pandas as pd
series = pd.Series([1, None, 3])
print(series.isna())The Pandas beginner guide covers missing values and cleaning.
Avoid AttributeError
This fails when user is None:
# print(user.name)Check the value at an appropriate boundary:
if user is None:
raise LookupError("user was not found")
print(user.name)Do not scatter unnecessary None checks throughout the code. Define clearly which functions may return it and handle it close to the call.
None in Conditions
discount = None
if discount is None:
final_price = original_price
else:
final_price = original_price - discountThis keeps a valid zero discount distinct from a missing discount.
Use a Guard Clause
def send_welcome_email(user: dict | None) -> None:
if user is None:
return
print(f"Sending email to {user['email']}")A guard clause handles the missing case early and keeps the main path less indented. Only use silent return when doing nothing is the correct contract.
Raise Instead of Returning None
For a required configuration, returning None may postpone the failure:
def require_setting(settings: dict, key: str) -> str:
value = settings.get(key)
if value is None:
raise KeyError(f"required setting is missing: {key}")
return valueThe exception handling guide explains when to raise and catch errors.
Test None Behavior
def test_find_user_returns_none_when_missing():
users = [{"id": 1, "name": "Lee"}]
assert find_user(users, 99) is NoneAlso test the non-missing path so the function's complete contract remains protected. See the Pytest guide.
Common Mistakes
- Comparing with
== Noneinstead ofis None. - Treating zero, empty text, and
Noneas the same state. - Forgetting that a function without return produces
None. - Assigning the result of
list.sort()or another in-place method. - Calling an attribute or method before checking an optional result.
- Using
dict.get()when missing key and stored None must be different. - Using
Noneas a sentinel when it is also a valid input. - Filtering with truthiness when zero or empty collections are valid values.
Practical Example: Optional Discount
def calculate_final_price(
price: float,
discount: float | None = None,
) -> float:
if price < 0:
raise ValueError("price cannot be negative")
if discount is None:
return price
if not 0 <= discount <= price:
raise ValueError("discount must be between zero and the price")
return price - discount
print(calculate_final_price(100.0))
print(calculate_final_price(100.0, 0.0))
print(calculate_final_price(100.0, 15.0))The three calls demonstrate missing discount, a valid zero discount, and a positive discount.
Frequently Asked Questions
Is None the same as null?
None is Python's absence value. Other systems use terms such as null, NULL, nil, or undefined, but their exact semantics may differ.
Why use is instead of ==?
None is a singleton, so identity expresses the intended check and avoids custom equality behavior.
Does None equal False?
No. Both are falsy in boolean contexts, but they are distinct values with different meanings.
Can a function return None?
Yes. It may return it explicitly, through a bare return, or by reaching the end without a return statement.
Should missing data always be None?
No. The correct representation depends on the domain, data source, and whether absence is normal, exceptional, or needs several distinct states.
Conclusion
Python None gives programs a clear representation for absence. Compare it with is None, distinguish it from other falsy values, document optional return types, and check it before using a result.
Use None when one missing state is enough. Use a custom sentinel, exception, or richer data model when the program must distinguish several forms of absence. Clear contracts prevent most None-related bugs.






