21.1 Understanding Errors
Errors are problems that stop a program from working correctly or cause it to produce the wrong result. Some errors are detected before the program runs, while others happen during execution. Learning to recognize error messages helps beginners find the exact line that caused a problem and understand what must be corrected.
Example
number = 10
text = "5"
print(number + int(text))
Output
15
Output Explanation
The text value "5" is converted to the integer 5 before addition. Python then adds 10 and 5 and displays 15. Without the conversion, Python would report an error because a number and a string cannot be added directly.
21.2 Syntax Errors
A syntax error happens when code does not follow Python's writing rules. Common causes include missing quotation marks, missing parentheses, incorrect indentation, or forgetting a colon. Python checks the program before running it, and if the syntax is invalid, the interpreter shows a message that points near the problem.
Example
message = "Hello"
print(message)
Output
Hello
Output Explanation
This corrected program follows Python syntax. The quotation marks are complete, the variable assignment is valid, and the print function has both parentheses. Python stores the text in message and then displays Hello.
21.3 Runtime Errors
A runtime error happens after Python has started running the program. The code may be written with correct syntax, but an operation fails while the program is executing. Examples include dividing by zero, opening a missing file, or converting unsuitable text into a number. Runtime errors are usually represented by exceptions.
Example
try:
result = 10 / 0
except ZeroDivisionError:
print("You cannot divide by zero.")
Output
You cannot divide by zero.
Output Explanation
Python tries to calculate 10 divided by zero. That operation raises ZeroDivisionError. The except block catches that specific exception, so the program displays a helpful message instead of stopping with a traceback.
21.4 Logical Errors
A logical error occurs when a program runs without crashing but gives an incorrect result. Python cannot automatically detect this type of problem because the code is valid. The programmer must compare the expected result with the actual result, examine the formula or conditions, and correct the program's logic.
Example
price = 20
quantity = 3
total = price * quantity
print(total)
Output
60
Output Explanation
The correct formula multiplies the item price by the quantity. Python calculates 20 times 3 and displays 60. If addition had been used instead, the program would run but produce the wrong answer, which would be a logical error.
21.5 Exceptions
An exception is an object that represents a problem occurring while a Python program runs. Different problems have different exception types, such as ValueError, TypeError, KeyError, and ZeroDivisionError. By identifying the exception type, a programmer can handle the problem in a controlled and understandable way.
Example
try:
age = int("twelve")
except ValueError:
print("Age must be written as digits.")
Output
Age must be written as digits.
Output Explanation
The int function cannot convert the word twelve into an integer, so Python raises ValueError. The except block handles that exception and prints a clear message for the user.
21.6 The `try` Block
The try block contains code that may cause an exception. Python runs the statements inside try normally. If no problem occurs, execution continues. If an exception happens, Python stops the remaining statements in that try block and searches for a matching except block that can handle the problem.
Example
try:
number = int("25")
print(number * 2)
except ValueError:
print("Invalid number")
Output
50
Output Explanation
The text 25 can be converted successfully into an integer. Python multiplies the number by 2 and displays 50. Because no ValueError occurs, the except block is skipped.
21.7 The `except` Block
The except block tells Python what to do when a particular exception occurs in the related try block. It prevents the program from ending suddenly and gives the programmer a place to show a helpful message, use a backup value, request new input, or safely continue another part of the program.
Example
try:
value = int("abc")
except ValueError:
print("Please enter a valid whole number.")
Output
Please enter a valid whole number.
Output Explanation
Converting abc to an integer fails and raises ValueError. Python immediately moves to the matching except block and displays the beginner-friendly message instead of showing an unhandled error.
21.8 Catching Specific Exceptions
Catching a specific exception means naming the exact error type after except. This is safer than catching every possible problem because each error can receive the correct response. For example, invalid numeric text should be handled differently from division by zero or a missing dictionary key.
Example
try:
number = int("0")
print(100 / number)
except ValueError:
print("Not a number")
except ZeroDivisionError:
print("The number cannot be zero")
Output
The number cannot be zero
Output Explanation
The conversion succeeds because 0 is valid numeric text. The division then raises ZeroDivisionError. Python skips the ValueError handler and runs the ZeroDivisionError handler.
21.9 Multiple Exception Handlers
A single try block can have several except blocks. Each handler can respond to a different type of failure. Python checks the handlers in order and runs the first compatible one. This makes programs clearer because invalid input, missing data, and calculation problems can each have their own explanation.
Example
data = {"price": 10}
try:
quantity = data["quantity"]
print(100 / quantity)
except KeyError:
print("Quantity is missing.")
except ZeroDivisionError:
print("Quantity cannot be zero.")
Output
Quantity is missing.
Output Explanation
The dictionary does not contain the key quantity, so Python raises KeyError before division occurs. The KeyError handler runs and displays the message. The ZeroDivisionError handler is not needed.
21.10 The `else` Block
The else block runs only when the try block finishes without raising an exception. It is useful for code that should execute after a risky operation succeeds. Keeping successful follow-up work in else makes it clear which statements are protected by try and which statements depend on success.
Example
try:
age = int("18")
except ValueError:
print("Invalid age")
else:
print("Age accepted:", age)
Output
Age accepted: 18
Output Explanation
The conversion to integer succeeds, so Python does not run the except block. It then enters the else block and prints the accepted age.
21.11 The `finally` Block
The finally block runs whether an exception happens or not. It is commonly used for cleanup actions, such as closing a file, releasing a network connection, or showing that an operation has finished. Even when the except block handles an error, Python still executes the finally block afterward.
Example
try:
print(10 / 2)
except ZeroDivisionError:
print("Division failed")
finally:
print("Calculation finished")
Output
5.0
Calculation finished
Output Explanation
The division succeeds and prints 5.0. No exception handler is needed. The finally block still runs and prints Calculation finished, showing that finally executes after the try process.
21.12 Raising Exceptions
Sometimes a program should deliberately create an exception when data breaks an important rule. Raising an exception tells the caller that the operation cannot continue normally. This is useful for validating ages, prices, passwords, quantities, and other values that must satisfy specific requirements.
Example
age = -3
try:
if age < 0:
raise ValueError("Age cannot be negative")
except ValueError as error:
print(error)
Output
Age cannot be negative
Output Explanation
The condition detects that age is below zero. The program raises a ValueError with a custom message. The except block catches the exception as error and prints that message.
21.13 The `raise` Statement
The raise statement creates an exception manually. It can raise a built-in exception or a custom exception. A message can be included inside the exception to explain the problem. After raise runs, normal execution stops until a suitable exception handler catches the raised exception.
Example
password = "abc"
try:
if len(password) < 8:
raise ValueError("Password must contain at least 8 characters")
except ValueError as error:
print(error)
Output
Password must contain at least 8 characters
Output Explanation
The password length is only three characters, so the condition is true. The raise statement creates ValueError. The handler prints the explanatory message stored in the exception.
21.14 Exception Arguments
Exception arguments are values passed into an exception object, usually to provide a helpful message or related details. When the exception is caught with the as keyword, the program can access these arguments. They help developers and users understand exactly why the operation failed.
Example
try:
raise ValueError("Invalid score", 150)
except ValueError as error:
print(error.args)
Output
('Invalid score', 150)
Output Explanation
The ValueError receives two arguments: a message and the invalid value. The args attribute stores them as a tuple. Printing error.args displays both pieces of information.
21.15 Exception Chaining
Exception chaining connects a new exception to the original exception that caused it. The from keyword makes this relationship explicit. Chaining is valuable when low-level technical errors need to be converted into clearer application errors while still preserving the original cause for debugging.
Example
try:
try:
number = int("abc")
except ValueError as original:
raise RuntimeError("Could not prepare the number") from original
except RuntimeError as error:
print(error)
Output
Could not prepare the number
Output Explanation
The failed conversion first raises ValueError. The program then raises RuntimeError from that original exception. The outer handler catches RuntimeError and prints the clearer application message.
21.16 Creating Custom Exceptions
A custom exception is a programmer-defined class used to represent a problem specific to an application. It normally inherits from Exception or one of its subclasses. Custom exceptions make error handling easier to understand because their names can clearly describe rules such as insufficient balance, invalid order, or expired membership.
Example
class InsufficientBalanceError(Exception):
pass
balance = 20
cost = 30
try:
if cost > balance:
raise InsufficientBalanceError("Not enough money")
except InsufficientBalanceError as error:
print(error)
Output
Not enough money
Output Explanation
The custom class represents a balance problem. Because the cost is greater than the balance, the program raises InsufficientBalanceError. The matching handler catches it and prints the message.
21.17 Exception Hierarchies
Python exceptions are organized in a class hierarchy. General exception classes are parents of more specific exception classes. For example, ZeroDivisionError is a kind of ArithmeticError, and ArithmeticError is a kind of Exception. Understanding this hierarchy helps programmers decide whether to catch one specific error or a broader group.
Example
try:
result = 5 / 0
except ArithmeticError:
print("A mathematical error occurred.")
Output
A mathematical error occurred.
Output Explanation
Dividing by zero raises ZeroDivisionError. Because ZeroDivisionError is a subclass of ArithmeticError, the broader ArithmeticError handler can catch it and display the message.
21.18 Assertions
An assertion checks whether a condition that the programmer expects to be true is actually true. Assertions are mainly development and debugging tools, not replacements for normal user-input validation. When the condition is false, Python raises AssertionError, which helps reveal incorrect assumptions inside the program.
Example
temperature = 20
assert temperature >= -100
print("Temperature accepted")
Output
Temperature accepted
Output Explanation
The assertion checks whether the temperature is at least negative 100. The condition is true, so no exception is raised. Python continues to the next line and prints Temperature accepted.
21.19 The `assert` Statement
The assert statement contains a condition and may include an optional message. If the condition is true, nothing happens and the program continues. If it is false, Python raises AssertionError. This is useful for checking internal assumptions while testing functions, calculations, and program states.
Example
items = ["pen", "book"]
assert len(items) > 0, "The list must not be empty"
print(items[0])
Output
pen
Output Explanation
The list contains two items, so the assertion succeeds. Python then accesses the first item and prints pen. If the list were empty, AssertionError would be raised with the supplied message.
21.20 EAFP vs LBYL
EAFP means it is easier to ask forgiveness than permission. In Python, this style tries an operation and handles an exception if it fails. LBYL means look before you leap, which checks conditions before acting. Both approaches are useful, but EAFP is often natural when failure is uncommon and exceptions are clear.
Example
data = {"name": "Ali"}
try:
print(data["name"])
except KeyError:
print("Name is missing")
Output
Ali
Output Explanation
The program directly tries to access the name key. Because the key exists, it prints Ali and no exception occurs. This is an EAFP approach because the operation is attempted first.
21.21 Exception Handling Best Practices
Good exception handling catches only errors that the program can meaningfully manage. Use specific exception types, provide clear messages, avoid hiding unexpected problems, and keep try blocks focused on risky statements. Cleanup belongs in finally, successful follow-up work can go in else, and custom exceptions should describe application-specific failures.
Example
try:
quantity = int("4")
except ValueError:
print("Quantity must be a whole number")
else:
total = quantity * 5
print("Total:", total)
Output
Total: 20
Output Explanation
Only the conversion is placed in the try block because that operation can raise ValueError. Since conversion succeeds, the else block calculates the total and displays 20. This keeps the handler specific and the code easy to read.
21.22 Chapter Practice Exercises
Practice exercises help you become comfortable reading tracebacks and handling common exceptions. Work slowly and test both successful and unsuccessful inputs. Try converting text to numbers, dividing values, accessing dictionary keys, validating ranges, and using try, except, else, and finally together. Compare each actual result with the result you expected.
Example
values = ["10", "five", "20"]
for value in values:
try:
print(int(value))
except ValueError:
print("Skipped:", value)
Output
10
Skipped: five
20
Output Explanation
The strings 10 and 20 can be converted to integers, so they are printed. The word five cannot be converted, causing ValueError. The handler prints Skipped: five, and the loop continues to the final value.
21.23 Chapter Mini Project
In this mini project, you will build a small safe calculator. The program converts text values into numbers, selects an operation, catches invalid numeric input, prevents division by zero, and reports unsupported operations. This project combines the major exception-handling tools from the chapter in one beginner-friendly example.
Example
first_text = "12"
second_text = "0"
operation = "/"
try:
first = float(first_text)
second = float(second_text)
if operation == "+":
result = first + second
elif operation == "-":
result = first - second
elif operation == "*":
result = first * second
elif operation == "/":
result = first / second
else:
raise ValueError("Unsupported operation")
except ValueError as error:
print("Input error:", error)
except ZeroDivisionError:
print("Calculation error: cannot divide by zero")
else:
print("Result:", result)
finally:
print("Calculator finished")
Output
Calculation error: cannot divide by zero
Calculator finished
Output Explanation
Both text values convert successfully to floating-point numbers. The selected operation is division, but the second number is zero, so ZeroDivisionError occurs. Its handler prints the calculation message. Finally then runs and confirms that the calculator has finished.