Debugging and Error Handling
Debugging is not only about reacting after a crash.
The Hidden Risk in Line Parsing
When a program parses a file line by line, it may split each line into words and then inspect particular words. The danger is that lines do not always have the same structure. A line may be empty, or it may contain only one word when the program expects more. Accessing a list index that does not exist causes an IndexError and crashes the program. Defensive control flow prevents the program from making that unsafe access.
The three techniques in this lesson work together: continue skips unwanted lines, guardian code checks that an access is safe, and or combines several skip conditions into one readable decision.
Skipping Unwanted Iterations
When parsing a file, you may care only about lines that begin with the word From. A continue statement handles the lines that do not qualify. When the condition for skipping is met, continue immediately jumps back to the top of the loop. The remaining statements in the current iteration are not executed, and the loop moves on to the next line.
for line in lines:
words = line.split()
if words[0] != "From":
continue
process_line(words)- 1The loop receives a line.
- 2split creates a list of words from that line.
- 3The condition checks the first word.
- 4continue returns control to the next loop iteration when the first word is not From.
- 5The processing step runs only for a line whose first word is From.
- Each iteration examines one line.
- The line is split into a list of words.
- A line whose first word is not From is skipped.
- Only qualifying lines reach the processing step.
Why the First Check Can Fail
What do you think happens?
What happens when an empty line reaches the expression words[0]?
Reveal answer
The program raises an IndexError before it can execute continue.
An empty line produces no words, so there is no element at index 0. The program must check that the list contains an element before accessing words[0].
The continue statement cannot protect an expression that fails before the condition is evaluated. In the earlier pattern, words[0] is evaluated as part of the if condition. If words is empty, the program crashes while trying to obtain its first element. The missing step is a guardian check placed before the access it protects.
Guarding List Access
Guardian code is a defensive check that verifies whether an operation is safe before allowing it to happen. For a list of words, the program checks the list length before accessing an index. If the list is empty, continue skips the line. Only after this check succeeds may the program safely inspect words[0].
Protecting the First-Word Check
A parser should process only lines whose first word is From, but some input lines may be empty.
- 1Split the line
The line becomes a list named words.
- 2Check the length
If
len(words)is 0, the line has no first word, so continue skips it before any index access. - 3Inspect the first word
The expression words[0] is reached only after the guardian check confirms that an element exists.
- 4Skip or process
A nonmatching first word causes another continue; a first word of From reaches the processing step.
The parser avoids accessing a nonexistent first element and processes only lines that meet the required criterion.
Combining Skip Conditions
Two separate if statements can both use continue: one for an empty list and one for a first word that is not From. This works, but the conditions can be combined. The or operator makes the complete condition true when at least one of its component conditions is true. Therefore, the line is skipped if it is empty or if its first word is not From.
| Line condition | Overall skip condition | Result |
|---|---|---|
| The list is empty | True or ... | continue skips the line |
| The first word is not From | ... or True | continue skips the line |
| The list is not empty and the first word is From | False or False | The line reaches processing |
The or expression is true when at least one skip condition is true.
A Complete Defensive Pattern
for line in lines:
words = line.split()
if len(words) == 0 or words[0] != "From":
continue
process_line(words)- 1The parser starts with one line and creates its words list.
- 2The empty-list check is positioned before the first-word access.
- 3If the list is empty, the line is skipped immediately.
- 4If the list is not empty but its first word is not From, the line is also skipped.
- 5If neither skip condition is true, processing continues.
- Read and split the current line.
- The first condition protects the index access for empty lines.
- The second condition filters out lines whose first word is not From.
- continue skips the current iteration when either condition is true.
- Only qualifying, nonempty lines are processed.
This pattern is compact without removing the safety check. The guardian condition comes first inside the compound expression, and the filtering condition follows it. The continue statement has one clear job: leave the current iteration whenever the line should not be processed.
Mistakes That Cause Crashes
Common mistake Accessing words[0] before checking whether the list has an element.
What it looks likeif words[0] != "From":Why it failsAn empty line produces an empty list, so index 0 does not exist and the program raises
IndexError.Do this insteadCheck the list length first, then access words[0] only when the list is not empty.
Common mistake Putting the guardian check after the protected access.
What it looks likeif words[0] != "From": continue if len(words) == 0: continueWhy it failsThe unsafe access has already happened before the length check can help.
Do this insteadPlace the guardian check before every access it protects.
Common mistake Processing every line without filtering.
What it looks likefor line in lines: process_line(line)Why it failsLines that do not meet the required criterion are processed instead of being skipped.
Do this insteadUse continue to move immediately to the next iteration when a line does not qualify.
Common mistake Repeating separate skip blocks when one compound condition is clearer.
What it looks likeif len(words) == 0: continue if words[0] != "From": continueWhy it failsThe logic works, but combining related skip conditions can reduce duplication and improve readability.
Do this insteadUse one if statement with or when either condition should cause the same continue action.
Practice the Control Flow
Rewrite a line-parsing loop so that it processes only nonempty lines whose first word is From. Use one compound if statement with or and place continue in that statement.
Hints
- Split each line into a list named words.
- The first condition should identify an empty list.
- The second condition should identify a first word that is not From.
- Both conditions should lead to the same continue statement.
Tracing Three Kinds of Lines
Predict the control-flow result for an empty line, a line beginning with To, and a line beginning with From.
- 1Empty line
Splitting produces no words. The length condition is true, so continue skips the iteration before words[0] is accessed.
- 2Line beginning with To
The list is not empty, but the first word is not From. The second condition is true, so continue skips the iteration.
- 3Line beginning with From
The list is not empty and the first word is From. Both skip conditions are false, so the line reaches processing.
Only the line beginning with From reaches the processing step, while the empty and nonmatching lines are skipped safely.
Reliable Parsing Habits
- Identify which lines your parser actually needs to process.
- Use continue to leave an iteration as soon as a line fails the required criterion.
- Check a list's length before accessing an index.
- Place guardian checks before the code they protect.
- Combine skip conditions with or when either condition should produce the same result.
- Test with empty lines and lines containing fewer words than expected.
Debugging is not only about reacting after a crash. In this pattern, error handling is built into the control flow: unsafe input is recognized early, the current iteration is abandoned, and only data that passes the checks is processed.
Key Takeaways
- continue immediately moves control to the next loop iteration, skipping the remaining statements for the current line.
- Accessing a nonexistent list index can raise
IndexError, especially when parsing empty or inconsistently structured lines. - Guardian code checks the list length before an index is accessed.
- The or operator makes a combined skip condition true when at least one component condition is true.
- Robust parsing places checks before protected operations and tests edge cases such as empty lines.