Plenty of Python beginners feel a sense of victory when their code finally runs without error messages. But a script that executes without complaint can still give the wrong answer, especially if it compares user input as strings instead of numbers.
How String Comparisons Lead to Logical Errors
Take a basic age-checking script. The aim is to print whether someone is an adult based on their age. A first draft often looks like this:
age = input("Age: ")
if age >= 18:
print("Adult")Running this code and entering 9 triggers a TypeError because input() returns a string, not an integer. Python blocks direct comparison between a string and an integer. The Python Software Foundation documentation points out that this is intentional: text input must be converted to a numeric type before mathematical comparison.
Some learners try to fix the error by changing the comparison to if age >= "18":. This stops the error, but brings in a subtle bug: the program now compares two strings, not numbers.
Unexpected Results from String Comparisons
When Python compares strings, it checks each character from left to right using their Unicode values. For example, "9" is compared to "18" by looking at the first character of each string. Since "9" (Unicode U+0039) comes after "1" (Unicode U+0031), Python treats "9" as greater than "18". This means the following code prints "Adult" even for a 9-year-old:
age = "9"
if age >= "18":
print("Adult")
else:
print("Minor")Python outputs "Adult" with no error, but the logic is off for age comparison. This happens because of lexicographic comparison, where the first differing character decides the outcome, not the numeric value of the whole string. For example, "100" < "18" evaluates as true because after matching the first character "1", the second character "0" is less than "8" in Unicode order, even though 100 is numerically greater than 18.
Why This Bug Is Hard to Spot
This issue can slip by if you only test typical values. Entering "17", "18", or "25" may give expected results. But try "5", "9", or "100" and the program mislabels ages. The bug is tricky because the code runs and prints output, but the logic is broken. The apparent correctness for two-digit numbers near the threshold comes from the first character ordering, not from a true numeric check. Single-digit ages like "2" through "9" all sort after "1" and get wrongly marked as adults. Three-digit values like "100" sort below "18" and get marked as minors.
AgeProgram saysCorrect?"5" | Adult | No
"9" | Adult | No
"17" | Minor | Yes
"18" | Adult | Yes
"25" | Adult | Yes
"100" | Minor | No
Testing only a handful of values can hide the problem. Broader testing uncovers the logical error caused by string comparison. This character-order behavior comes from Unicode code points, where the decimal digit characters "0" through "9" sit in order, and "9" always sorts after "1". This is a property of the characters themselves, not a special rule for numbers or ages.
How to Correctly Compare Ages in Python
The fix is to convert the input string to an integer before comparing. Use int() to turn the text input into a number:
age = int(input("Age: "))
if age >= 18:
print("Adult")
else:
print("Minor")Now, entering 9 correctly prints "Minor". You can check the type before and after conversion:
age = "9"
print(type(age)) # <class 'str'>
age = int("9")
print(type(age)) # <class 'int'>Keep in mind that int() only works with strings that represent whole numbers. Passing "nine" to int() raises a ValueError. Solid programs should handle such cases using try/except ValueError blocks to avoid crashes and to check that the input is a reasonable age (for example, not negative or unreasonably large). The Python Software Foundation documentation stresses that numeric-looking text stays as text until you convert it, and that input validation is key for reliable applications.
Developing Better Debugging Habits
To avoid silent bugs like this, check your data types using type(), fix the root cause of errors instead of just removing error messages, and test a range of input values, including edge cases. These habits help make sure your code works as intended, not just that it runs without errors. This lesson applies to age checks and to any data received from text-based interfaces, such as prices, dates, and identifiers.
For more on practical debugging and code refactoring, see this earlier breakdown of Python code maintenance.
