042. Else Statement
Handle the default case explicitly
042. Else Statement
RAM Manager: Process Status
Aryan’s RAM manager needs to respond to process status checks. When a process is alive and consuming RAM, report it. When it’s gone, report that too.
The silent None return bug is one of the most common Python mistakes:
def check_process(pid, processes):
if pid in processes:
return f"PID {pid}: {processes[pid]}MB"
# ← if pid is NOT in processes, returns None silently
# caller does: status = check_process(999, procs)
# then: print(status) → "None" (confusing!)Aryan’s dispense function has this exact bug — it returns None when the item is out of stock instead of the error message. The tests catch the missing else.
💡 Fun fact: Implicit None returns are one of the most common sources of AttributeError: 'NoneType' object has no attribute '...' errors in Python. This error is so frequent that a popular saying in the Python community is: “Explicit is better than implicit” — the very first line of The Zen of Python (import this), written by Tim Peters in 1999.
⚠️ Watch out: A function missing an else branch doesn’t raise an error — it silently returns None. Your code will appear to work until something tries to use the return value, at which point you get a confusing TypeError or AttributeError far from the actual bug. Always ask: “What does this function return when the if condition is False?”
🤔 Think about it: Every if without an else has an implicit “do nothing and return None” branch. Is there ever a situation where returning None from a function is the correct, intentional design — and how would you communicate to callers that None is a valid return value rather than a bug?
Learning objectives
- Add else to handle the default/fallback case
- Avoid implicit None returns from missing else branches
- Use else to make all code paths explicit
Key concepts
- else
- default case
- explicit branching
Try it
Concept detail
Python functions that don’t explicitly return a value return None. This is legal but dangerous — callers may not check for None.
Without else: def dispense(item, stock): if item in stock: return “Dispensing “ + item # falls off the end → returns None implicitly
With else: def dispense(item, stock): if item in stock: return “Dispensing “ + item else: return “Out of stock” # explicit, can’t be accidentally None
Every if/elif chain should ask: “what happens when NONE of my conditions are True?”
- If None is acceptable, document it with a comment
- If it’s a bug, add an else with an explicit return or raise an exception
else on loops (unusual but valid): for item in items: if found_it(item): break else: # runs only if loop completed WITHOUT hitting break print(“not found”)
This for/else pattern is rare but useful for search loops.
Solution
def dispense(item, stock):
if item in stock and stock[item] > 0:
stock[item] -= 1
return "Dispensing " + item
else:
return "Sorry, " + item + " is out of stock"
def max_of_two(a, b):
if a >= b:
return a
else:
return bTests
def test_dispense_available():
stock = {"cola": 3, "water": 1}
assert dispense("cola", stock) == "Dispensing cola"
def test_dispense_reduces_stock():
stock = {"cola": 3}
dispense("cola", stock)
assert stock["cola"] == 2
def test_dispense_out_of_stock():
stock = {"cola": 0}
result = dispense("cola", stock)
assert result == "Sorry, cola is out of stock", f"Got {repr(result)} — missing else branch returns None"
def test_dispense_not_in_stock():
stock = {"cola": 3}
result = dispense("juice", stock)
assert result == "Sorry, juice is out of stock", f"Got {repr(result)} — missing else branch returns None"
def test_dispense_none_check():
stock = {"cola": 0}
result = dispense("cola", stock)
assert result is not None, "Function returned None — add an else branch"
def test_max_of_two():
assert max_of_two(5, 3) == 5
assert max_of_two(3, 5) == 5
assert max_of_two(5, 5) == 5