Compile vs Runtime Errors¶
Programming errors fall into three main categories based on when they occur: compile-time errors (caught before execution), runtime errors (caught during execution), and logical errors (never caught by Python).
Compile-Time Errors¶
Compile errors occur during the compilation or parsing phase before the program runs. In Python, these are primarily syntax errors.
# SyntaxError: Missing colon
if True
print("Hello")
# SyntaxError: Invalid syntax
x = 5 +
# SyntaxError: Unmatched parenthesis
print("Hello"
Python catches these errors when it parses the code, before any execution begins.
Syntax Error Examples¶
# Missing colon after if
if x > 5
print(x)
# IndentationError (a type of SyntaxError)
def foo():
print("Hello")
# Invalid assignment
5 = x
# Unclosed string
message = "Hello
Compile Errors Prevent Execution¶
With a syntax error, no code runs at all.
print("This never prints")
if True # SyntaxError here
print("Hello")
print("This also never prints")
None of the print statements execute because Python fails to parse the file.
Compile Errors Cannot Be Caught¶
Syntax errors cannot be caught with try/except in the same file.
# This doesn't work
try:
if True # SyntaxError
pass
except SyntaxError:
print("Caught") # Never reached
The entire file fails to parse, so try/except never executes.
Runtime Errors¶
Runtime errors occur during program execution when the code encounters a situation it cannot handle.
# ZeroDivisionError
result = 10 / 0
# TypeError
result = "5" + 5
# IndexError
lst = [1, 2, 3]
print(lst[10])
# KeyError
d = {"a": 1}
print(d["b"])
# FileNotFoundError
f = open("nonexistent.txt")
Common Runtime Error Types¶
| Error | Cause |
|---|---|
ZeroDivisionError |
Division by zero |
TypeError |
Wrong type for operation |
ValueError |
Right type, wrong value |
IndexError |
List index out of range |
KeyError |
Dictionary key not found |
AttributeError |
Object has no attribute |
NameError |
Name not defined |
FileNotFoundError |
File does not exist |
Runtime Errors Allow Partial Execution¶
With runtime errors, code runs until the error occurs.
print("This prints") # Executes
x = 10 / 0 # RuntimeError here
print("This never prints") # Never reached
Output:
This prints
ZeroDivisionError: division by zero
Handling Runtime Errors¶
Runtime errors can be caught and handled with try/except.
try:
result = 10 / 0
except ZeroDivisionError:
print("Cannot divide by zero")
result = 0
print(f"Result: {result}")
Output:
Cannot divide by zero
Result: 0
Forward Referencing Errors¶
Forward referencing errors occur when code attempts to use a variable, function, or class before it has been defined. These are runtime errors (NameError) caused by Python's sequential execution model.
Variable Forward Reference¶
Using a variable before assignment raises NameError.
print(x) # NameError: name 'x' is not defined
x = 10
Fix: Define variables before use.
x = 10
print(x) # 10
Function Forward Reference¶
Calling a function before its definition raises NameError.
result = add(5, 3) # NameError: name 'add' is not defined
def add(a, b):
return a + b
Fix: Define functions before calling them.
def add(a, b):
return a + b
result = add(5, 3)
print(result) # 8
Why Forward Referencing Errors Occur¶
Python processes code sequentially:
- The interpreter reads code line by line
- Names are added to the namespace when assigned
- Referencing a name requires it to already exist
# At this point, 'x' is not in the namespace
print(x) # NameError
# Now 'x' is added to the namespace
x = 10
Functions Can Reference Each Other¶
Function bodies are not executed until called, so mutual references work.
def is_even(n):
if n == 0:
return True
return is_odd(n - 1) # is_odd not defined yet, but OK
def is_odd(n):
if n == 0:
return False
return is_even(n - 1)
print(is_even(4)) # True
This works because is_odd is only looked up when is_even is actually called, by which time both functions exist.
Default Parameter Pitfall¶
A common forward referencing error occurs when using self in default parameter values.
class BlackScholes:
def __init__(self, T):
self.T = T
# ERROR: self doesn't exist when default is evaluated
def run_MC(self, num_steps=int(self.T * 12 * 21)):
pass
This fails because default parameter values are evaluated at function definition time, not at call time. At definition time, self doesn't exist yet.
Fix: Use None as default and set the value inside the method.
class BlackScholes:
def __init__(self, T):
self.T = T
def run_MC(self, num_steps=None):
if num_steps is None:
num_steps = int(self.T * 12 * 21)
# ... method body
Logical Errors¶
A third category exists: logical errors. The code runs without exceptions but produces incorrect results.
# Logical error: wrong formula
def average(a, b):
return a + b # Should be (a + b) / 2
result = average(10, 20)
print(result) # 30, but should be 15
Logical errors are the hardest to find because Python cannot detect them.
Key Differences Summary¶
| Feature | Compile Error | Runtime Error | Logical Error |
|---|---|---|---|
| When detected | Before execution | During execution | Never (by Python) |
| Program runs? | No | Partially (until error) | Yes, completely |
| Type in Python | SyntaxError |
Various exception types | None |
| Can be caught? | No | Yes, with try/except |
No |
| Detection method | Parser | Interpreter | Testing/review |
Best Practices¶
-
Define variables before use
x = 10 print(x) -
Define functions before calling
def greet(): print("Hello") greet() -
Use
Nonefor instance-dependent defaultsdef method(self, value=None): if value is None: value = self.default_value -
Organize code with definitions first
# Constants MAX_SIZE = 100 # Functions def process(): pass # Main execution if __name__ == "__main__": process()