012. None Type
Represent intentional absence of a value
012. None Type
Aryan’s RAM monitor needs to look up a process by PID. If the PID doesn’t exist in the process table, what should the function return?
In C, he’d return NULL — a pointer to nothing. Python’s equivalent is None.
He first returns 0 when a process isn’t found. But 0 is a valid PID on some systems, and the caller can’t tell the difference between “process 0” and “not found”.
He then tries "" (empty string). But then the caller has to know to check for an empty string — and what if a process name is legitimately empty?
None is the unambiguous signal: this slot intentionally has no value. The caller checks if result is None and knows exactly what happened.
Fix the student lookup function to return None instead of "" when no match exists.
💡 Fun fact: None is a singleton in Python — there is exactly one None object in the entire runtime. Every None value in every module refers to the same object. This is why is None is the correct check: you are verifying object identity, not value equality.
⚠️ Watch out: Returning 0, "", or [] as a “not found” sentinel is a classic bug — these values are falsy, so if result: may seem to work, but they are also valid real results. Only None unambiguously means “absent”; everything else is a real value that might legitimately be empty or zero.
🤔 Think about it: Functions that do not explicitly return a value implicitly return None. How could this silently break code that calls such a function and assigns its return value to a variable?
Learning objectives
- Use None to represent absent/missing values
- Return None explicitly from functions when appropriate
- Check for None using the ‘is None’ idiom
Key concepts
- None
- null value
- identity check
Try it
Concept detail
None is Python’s null value — it represents intentional absence, not an empty or zero value. There is exactly ONE None object in the entire Python runtime (it’s a singleton).
None is falsy: bool(None) == False. But it is distinct from every other falsy value: None != 0 None != False None != “” None != []
Always check for None with identity operators: if result is None: ← correct if result is not None: ← correct if result == None: ← works but considered bad style (PEP 8)
Why is None a singleton? Because there is no meaningful distinction between “one absence” and “another absence”. Any two is-None checks are checking the same object.
Functions that don’t explicitly return a value implicitly return None: def f(): x = 1 print(f()) # prints None
None as a default argument: def f(x=None) is the standard sentinel pattern that avoids the mutable default argument bug (def f(x=[]) is dangerous).
Solution
def find_user(users, username):
for user in users:
if user["name"] == username:
return user
return NoneTests
USERS = [
{"name": "alice", "age": 30},
{"name": "bob", "age": 25},
]
def test_found_user():
result = find_user(USERS, "alice")
assert result == {"name": "alice", "age": 30}
def test_not_found_returns_none():
result = find_user(USERS, "charlie")
assert result is None
def test_not_found_is_not_empty_string():
result = find_user(USERS, "charlie")
assert result != ""
def test_bob_found():
result = find_user(USERS, "bob")
assert result["age"] == 25
def test_none_is_falsy():
result = find_user(USERS, "nobody")
assert not result