| name | return-none-for-failure |
| description | For graceful failure: return None or False instead of exceptions, let caller decide how to handle failure. |
return-none-for-failure
When to Use
- Algorithm might not find solution
- Search can fail naturally
- Caller should check result
- Backtracking search patterns
When NOT to Use
- Failure is a programming error (raise exception)
- Caller must handle failure (exception is clearer)
- None is a valid result
The Pattern
Return None (or False) to indicate failure, let caller check.
def find(items, predicate):
"""Find first item matching predicate, or None."""
for item in items:
if predicate(item):
return item
return None
result = find(items, is_valid)
if result is not None:
process(result)
else:
handle_not_found()
Example (from pytudes)
def search(values):
"""Using depth-first search and propagation."""
if values is False:
return False
if all(len(values[s]) == 1 for s in squares):
return values
n, s = min((len(values[s]), s)
for s in squares if len(values[s]) > 1)
for d in values[s]:
result = search(assign(values.copy(), s, d))
if result:
return result
return False
def eliminate(values, s, d):
"""Return values, or False if contradiction detected."""
if d not in values[s]:
return values
values[s] = values[s].replace(d, '')
if len(values[s]) == 0:
return False
return values
solution = solve(puzzle)
if solution:
display(solution)
else:
print("No solution exists")
def first(iterable):
"""First element of iterable, or None."""
return next(iter(iterable), None)
solution = first(solve('SEND + MORE = MONEY'))
if solution:
print(solution)
Key Principles
- False/None for "not found": Not an exception
- Caller checks with
if: Natural control flow
- Propagate failure:
if result is False: return False
- Truthy check works:
if result: catches None/False
- Document the contract: Docstring says what failure means