Input Validation and Sanitization
Defensive programming means asking 'What could go wrong?' before your program reaches users.
The Empty-Input Crash
Imagine a program that reads lines from a user. It should skip lines beginning with a hash symbol, stop when the user enters the word done, and print every other line. With ordinary input, this logic appears to work. The problem appears when the user presses Enter without typing anything. That empty line can cause the program to crash before it has a chance to handle the input.
Defensive programming means asking what could go wrong before a program reaches users, then anticipating edge cases and handling them gracefully instead of allowing the program to fail.
An empty string contains zero characters. It therefore has no character at index 0.
Tracing the Missing Character
Suppose the program checks whether a line begins with a hash by asking for the character at index 0. For a line such as hello there, index 0 exists and contains h. For a line such as #comment, index 0 exists and contains #. For an empty line, however, there is no character to retrieve. Attempting to access index 0 raises IndexError with the message string index out of range.
What do you think happens?
A user submits an empty line. What happens when the program tries to inspect its first character?
Reveal answer
Answer: Python raises IndexError.
The empty string has no character at index 0, so direct indexing attempts to retrieve a position that does not exist.
Two Safe Prefix Checks
There are two distinct ways to prevent this crash. The first is to use startswith() when the actual goal is to check a prefix. The second is to use the guardian pattern when direct indexing is needed for a reason beyond a simple prefix check.
| Technique | How it protects the program | Best fit |
|---|---|---|
| startswith() | Checks whether the string begins with a specified substring without manually retrieving index 0 | Checking whether a line starts with a prefix such as a hash symbol |
| Guardian pattern | Checks a safety condition before allowing a later condition to access an index | Situations where direct indexing is needed after confirming that the string is long enough |
For prefix checking, startswith() directly expresses the question the program is asking: does this string start with a hash? It returns True for a string such as #comment, False for a string such as hello, and False for an empty string. It handles the empty case without raising IndexError.
Guarding an Index Access
The guardian pattern places a safety check before a potentially unsafe operation. In this case, the first condition checks whether the line has more than zero characters. Only if that condition is true should the program evaluate the condition that examines index 0.
The guardian works because Python evaluates boolean expressions from left to right. With an and expression, a False result on the left is enough to determine that the whole expression cannot be True. Python therefore stops and does not evaluate the right side. For an empty line, the length check is False, so the index check is never reached and IndexError is avoided.
Choosing the Clearer Technique
For the specific task of checking whether a string starts with a prefix, startswith() is usually the clearer choice. It is shorter, directly communicates the intended question, and safely handles empty strings. The guardian pattern remains valuable when later logic genuinely needs to access an index after confirming that the string is long enough.
Assuming every user input contains at least one character.
The resulting empty string has no index 0, so direct access to that position raises IndexError.
Fix:
Use startswith() for prefix checks or place a length guard before indexing.Indexing before checking whether the string is nonempty.
The dangerous operation occurs before the protection can take effect.
Fix:
Evaluate the guard condition first so short-circuit evaluation can prevent the index access.Using direct indexing when the real intention is only to test a prefix.
Manual indexing creates an unnecessary failure point for empty input.
Fix:
Use startswith() because it expresses the prefix-checking task directly and safely.Testing only the happy path.
The missing edge case allows a user action to expose a crash.
Fix:
Ask what unexpected input a user might provide and account for it before shipping.
Apply the Guard
A line-processing program must distinguish three inputs: an empty line, a line beginning with a hash, and ordinary text. Explain which technique you would choose for checking the hash prefix and describe how the other technique would prevent an error if direct access to the first character were required.
Hints
- For a direct prefix question, consider the method designed to check whether a string begins with a substring.
- For direct indexing, place the length check before the index check.
- Remember that and stops evaluating when its left side is False.
A strong answer identifies startswith() as the clearer technique for checking a hash prefix. It also explains that the guardian pattern first verifies that the line has more than zero characters. If the line is empty, the first condition is False and short-circuit evaluation prevents the index operation from running.
Key Takeaways
- Directly accessing index 0 fails when a string is empty because no character exists at that position.
- Defensive programming means anticipating edge cases before users encounter them.
- startswith() safely checks prefixes and returns False for an empty string.
- The guardian pattern checks string length before allowing an index access.
- Short-circuit evaluation prevents the right side of an and expression from running when the left side is False.