Working Examples by Hand and Finding Edge Cases
18 min · Intermediate
Need help? Ask a tutor on WhatsApp (opens in a new tab)A solution that works on the sample can still fail on a hidden test. The fix is a habit: trace a small example by hand, then hunt the edge cases on purpose.
Trace one example before you code
Dry run is working through code or an idea by hand on a small input, writing down every variable's value after each step, to see what it really does.
Take the smallest example that still shows the rule. Follow it step by step on paper and write each variable's value. The trace becomes the loop you will write.
sales = [5, 3, 8, 8, 2, 9]best = sales[0]for day, amount in enumerate(sales): if amount > best: best = amount print(day, amount, best)Example 1 · A hand trace turned into prints
marks = [40, 75, 75, 60]passed = 0for mark in marks: if mark >= 50: passed += 1 print(mark, passed)40 0 75 1 75 2 60 3
What this code does
- Each printed line matches one row you would write on paper.
- The count moves only when the mark is 50 or more, so 40 leaves it at 0.
Each line is one row of the trace table. You can see the highest sale so far change only on days 2 and 5.
Try the small and strange inputs
Edge case is an input at the limit of what the problem allows, such as an empty list, one item or all equal values, where code most often breaks.
Bugs hide at the edges: an empty input, one item, all items equal, negative numbers, the largest size allowed. Write these cases down before the code and run each one.
def average(values): if not values: return 0.0 return sum(values) / len(values)
for case in ([], [5], [2, 2, 2], [-3, 3]): print(case, average(case))Example 2 · max() of an empty list
orders = []try: print(max(orders))except ValueError as error: print("ValueError:", error)print(max(orders, default=0))ValueError: max() iterable argument is empty 0
What this code does
- An empty input is the classic edge case:
maxhas nothing to compare and raises ValueError. default=0gives a safe answer when the list may be empty.
Without the first check, the empty list would divide by zero. The edge-case list found that before a judge did.
Write the checks first with assert
assert statement stops the program with AssertionError when its condition is false. It is a one-line test for a case whose answer you already know.
Once you know the answers for your cases, turn them into assert lines. If the code is wrong, the program stops at the first failing line.
def second_largest(values): distinct = sorted(set(values)) return distinct[-2] if len(distinct) >= 2 else None
assert second_largest([4, 9, 7]) == 7assert second_largest([9, 9, 4]) == 4assert second_largest([5]) is Noneassert second_largest([]) is Noneprint("all checks passed")Example 3 · Second largest with repeats
values = [9, 9, 4]print(sorted(values)[-2])print(sorted(set(values))[-2])9 4
What this code does
- The first line returns 9 because the top value appears twice. It is the wrong answer for "second largest value".
- A set first removes the repeats and gives 4.
Example 4 · Starting the best value at zero
changes = [-5, -2, -9]wrong = 0right = changes[0]for change in changes: wrong = max(wrong, change) right = max(right, change)print(wrong, right)0 -2
What this code does
- Starting at 0 hides the fact that every value is negative, so
wrongstays 0 although no change is 0. - Starting from the first item always works when the list is not empty.
The set removes repeats, so [9, 9, 4] gives 4, not 9. The one-item and empty cases return None.
Watch the boundaries
Off-by-one mistakes come from ranges and slices. With 6 days and windows of 3 days there are 4 windows, not 3 and not 6. Count by hand, then compare with the code.
days = [3, 1, 4, 1, 5, 9]k = 3for start in range(len(days) - k + 1): window = days[start:start + k] print(start, window, sum(window))Example 5 · Windows longer than the list
days = [3, 1, 4]for k in [2, 3, 5]: print(k, max(0, len(days) - k + 1))2 2 3 1 5 0
What this code does
- With k = 5 the formula gives -1, which is meaningless.
max(0, ...)turns it into 0 windows. - Check the case where the window is bigger than the data.
The number of windows is len(days) - k + 1. The slice days[start:start + k] stops before start + k, so it holds exactly 3 items.
Next step
Check your understanding
10 quick questions