[Ch 7] Everything About Python Exception Handling from the Official Documentation

박주원
July 19, 2025

[Ch 7] Everything About Python Exception Handling from the Official Documentation

Use Original Cover Image
Type
Post
Children
Language
en
Tags
Python
Exception
except
finally
with
ExceptionGroup
add_note
try
Authors
박주원
Published
July 19, 2025

1. Error vs. Exception

1.1 Error

while True print('Hello world')
page icon
File "path/to/folder/code.py", line 1 while True ^ SyntaxError: expected ':'
An error is a fatal error that occurs at the system level of a program. If a program has an error, it cannot be compiled, which means it cannot be executed.
Errors that exist in Python include SyntaxError and its child classes.
A SyntaxError is an error that occurs when Python code does not conform to the syntax, such as omitting the : in a while statement.
 

1.2 Exception

10 * (1/0)
 
 
page icon
Traceback (most recent call last): File "path/to/folder/code.py", line 1, in <module> 10 * (1/0) ~^~ ZeroDivisionError: division by zero
4 + spam*3
page icon
Traceback (most recent call last): File "path/to/folder/code.py", line 1, in <module> 4 + spam*3 ^^^^ NameError: name 'spam' is not defined
'2' + 2
 
 
page icon
Traceback (most recent call last): File "path/to/folder/code.py", line 1, in <module> '2' + 2 ~~~~^~~ TypeError: can only concatenate str (not "int") to str
An exception is an error that can potentially occur during the runtime of a program. Even if an exception can occur, the program can be compiled and is ready to be executed.
If an unhandled exception occurs during the execution of the program, the program prints an error message and stops execution.
There are many types of exceptions in Python, such as ZeroDivisionError, NameError, and TypeError.
In addition to these built-in exceptions, users can also define their own exceptions.
In Python, exceptions often have "Error" in their names, even though they are exceptions and not errors.
 

1.3 Error vs. Exception

The differences between errors and exceptions can be summarized in the following table:
Error
Exception
Program Executability
Cannot be executed if an error occurs
Can be executed even if an exception may occur
Occurrence Timing
Compile time, before execution
Runtime, during execution
Impact of Occurrence
Program cannot be compiled
Program execution stops
Example in Python
SyntaxError and its child classes
Built-in classes like NameError and user-defined exception classes
 

2. Exception Handling

2.1 tryexcept statement

while True: try: x = int(input("Please enter a number: ")) break except ValueError: print("That was not a valid number. Try again...")
page icon
Please enter a number: asdf That was not a valid number. Try again... Please enter a number: 3
Exception handling in Python can be done using the try and except statements.
If a program encounters a try statement, it executes the code block inside the try statement. The program's handling differs depending on how an exception occurs during the execution of that code:
  • If no exception occurs within the try statement, the program proceeds to the next code outside the try statement.
  • If an exception occurs within the try statement and it matches the exception class attached to the except statement linked to the try statement, the program stops executing the try statement and executes the corresponding except statement.
  • If an exception occurs within the try statement but is not handled by any except statement, the program stops execution.
If a nested tryexcept structure is used, an unhandled exception in an except statement is considered to have occurred in the outer try statement and is passed to the outer except statement.
 

2.2 Matching Multiple Exception Types

try: # code except (RuntimeError, TypeError, NameError): # Handle RuntimeError, TypeError, NameError
try: # code except RuntimeError: # Handle RuntimeError except TypeError: # Handle TypeError except NameError: # Handle NameError
An except statement can match not just one exception class, but multiple exception classes for a single try statement.
The first example above uses a single except statement that matches three exception classes: RuntimeError, TypeError, and NameError for a single try statement.
The second example uses three separate except statements, each matching RuntimeError, TypeError, and NameError respectively, for a single try statement.
The difference between the second example and the first is that you can handle each exception class separately.
 

2.3 Exception and as

try: x = 1 / 0 except Exception as exc: print('type:', type(exc)) print('args:', exc.args) print('__str__:', exc)
page icon
type: <class 'ZeroDivisionError'> args: ('division by zero',) __str__: division by zero
All exceptions in Python inherit from the BaseException class, and all non-fatal exceptions inherit from the Exception class, which is a child class of BaseException.
The except statement handles exceptions by matching exception class patterns. Since almost all exceptions inherit from the Exception class, you can use the Exception pattern in an except statement to match general exceptions.
 
You can also use as in an except statement to get an instance of the exception class that occurred.
As shown in the example, the type of the exception instance is the class of that exception, and the args attribute is a tuple containing detailed information about the exception.
You can see the args of the instance by printing the exception instance to check its __str__ attribute.
 

2.4 except statement and Inheritance

try: # code except Exception: print("Exception occured") except RuntimeError: print("RuntimeError occured") except NotImplementedError: print("NotImplementedError occured")
Among Python's built-in exception classes, the NotImplementedError class inherits from the RuntimeError class, and the RuntimeError class, as mentioned earlier, inherits from the Exception class.
Since the except statement performs pattern matching on exception classes, the Exception pattern will match both RuntimeError and NotImplementedError.
Therefore, in the example above, if a NotImplementedError or RuntimeError occurs inside the try statement, the pattern in the top except Exception: will match, and in both cases, print("Exception occured") will be executed.
 
try: # code except NotImplementedError: print("NotImplementedError occured") except RuntimeError: print("RuntimeError occured") except Exception: print("Exception occured")
Conversely, in this code, if a NotImplementedError occurs, print("NotImplementedError occured") is executed, and if a RuntimeError occurs, print("RuntimeError occured") is executed.
 

2.5 else statement

try: x = int(input("Please enter a number: ")) except ValueError: print("That was not a valid number. Exiting program.") else: print(f"The number you entered is {x}.")
page icon
Please enter a number: asdf That was not a valid number. Exiting program.
page icon
Please enter a number: 3 The number you entered is 3.
In exception handling, an else statement can follow a tryexcept statement.
The code block of the else statement is executed only if no exception occurs in the corresponding try statement.
Typically, the else statement contains code that should be executed when the try statement, which might have caused an exception, runs successfully, such as closing a file handler opened in the try block.
 

3. raise statement

raise NameError("Hello")
page icon
Traceback (most recent call last): File "path/to/folder/code.py", line 1, in <module> raise NameError("Hello") NameError: Hello
In Python, you can directly raise an exception using the raise statement.
raise is followed by an instance of a specific exception class, such as NameError, which raises that exception instance.
 
try: x = int(input("Please enter a positive number: ")) if x <= 0: raise Exception("A non-positive number was entered.") except Exception as exc: print("error:", exc)
page icon
Please enter a positive number: -8 error: A non-positive number was entered.
As described above, since all general exceptions are derived classes of the Exception class, you can also raise a general exception like raise Exception("Error message").
 
try: raise NameError('Hello') except NameError: print('NameError occured') raise
page icon
NameError occured Traceback (most recent call last): File "path/to/folder/code.py", line 2, in <module> raise NameError('Hello') NameError: Hello
If you want to re-raise an exception within an except block, you can simply add raise at the end of the except block to re-raise the exception that occurred.
 
raise 문은 예외를 다음 tryexcept 문으로 “올린다”는 점에서 그 이름이 raise 문이라고 이해하면 간단하다.
You can understand the raise statement by thinking of it as "raising" the exception to the next tryexcept block.
The raise statement allows the programmer to raise specific exceptions in specific situations, making the program more sophisticated.
 

4. Exception Chaining

4.1 Exceptions inside except blocks

try: open("non_existent_file.txt") except OSError: raise RuntimeError("unable to handle error")
page icon
Traceback (most recent call last): File "path/to/folder/code.py", line 2, in <module> open("non_existent_file.txt") ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ FileNotFoundError: [Errno 2] No such file or directory: 'non_existent_file.txt' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "path/to/folder/code.py", line 4, in <module> raise RuntimeError("unable to handle error") RuntimeError: unable to handle error
When handling an exception, another exception may occur within the except block that is trying to handle it.
If an exception occurs inside an except block, a message is added to this exception indicating that the exception could not be handled, and it then behaves like a normal exception outside the tryexcept block.
 

4.2 from statement

try: open("non_existent_file.txt") except OSError as exc: raise RuntimeError("unable to handle error") from exc
page icon
Traceback (most recent call last): File "path/to/folder/code.py", line 2, in <module> open("non_existent_file.txt") ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ FileNotFoundError: [Errno 2] No such file or directory: 'non_existent_file.txt' The above exception was the direct cause of the following exception: Traceback (most recent call last): File "path/to/folder/code.py", line 4, in <module> raise RuntimeError("unable to handle error") from exc RuntimeError: unable to handle error
If you are re-raising an exception from within an except block using the raise statement, you can use the from statement to indicate that the exception being raised is a direct consequence of another exception.
The from statement follows the raise statement and takes an exception instance.
By adding a from statement to a raise statement, you can indicate that the exception being raised by raise is caused by the exception instance in the from clause.
 
try: open("non_existent_file.txt") except OSError: raise Exception("another exception") from None
page icon
Traceback (most recent call last): File "path/to/folder/code.py", line 4, in <module> raise Exception("another exception") from None Exception: another exception
If you want to raise an exception with a raise statement, but the exception is not related to any other exception, you can use from None to indicate this.
Using from None allows you to raise an exception without it being chained to another exception.
 

5. User-defined Exceptions

class MyError(Exception): pass try: raise MyError('An error occurred') except MyError as e: print(f'Caught an error: {e}')
page icon
Caught MyError: An error occurred
Programmers can define their own custom exceptions.
User-defined exceptions can be created by defining a class that inherits from the Exception class.
It is common practice to name exception classes with a suffix of "Error".
 

6. finally statement

6.1 finally statement basics

try: int(input('Please enter a number: ')) except ValueError: print('That was not a valid number.') finally: print('Hello, World!')
page icon
Please enter a number: asdf That was not a valid number. Hello, World!
page icon
Please enter a number: 3 Hello, World!
If a try statement has a finally clause, the code in the finally clause is executed last when the entire try statement finishes.
This is executed unconditionally at the end, before the try statement is completed, regardless of whether an exception occurred in the try statement.
Typically, the finally clause is used in situations that must be handled last, whether the try statement executed successfully or not, such as releasing external resources.
 

6.2 Re-raising exceptions

try: print('try clause') raise Exception('An error occurred') finally: print('finally clause')
page icon
try clause finally clause Traceback (most recent call last): File "path/to/folder/code.py", line 3, in <module> raise Exception('An error occurred') Exception: An error occurred
If an exception occurs inside a try statement with a finally clause but is not handled by an except clause, the finally clause is executed first, and then the exception is re-raised.
try: print('try clause') int(input('Please enter a number: ')) except ValueError: print('except clause') raise Exception('exception on "except"') else: print('else clause') raise Exception('exception on "else"') finally: print('finally clause')
page icon
try clause Please enter a number: asdf except clause finally clause Traceback (most recent call last): File "path/to/folder/code.py", line 3, in <module> int(input('Please enter a number: ')) ~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ValueError: invalid literal for int() with base 10: 'asdf' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "path/to/folder/code.py", line 6, in <module> raise Exception('exception on except') Exception: exception on "except"
page icon
try clause Please enter a number: 3 else clause finally clause Traceback (most recent call last): File "path/to/folder/code.py", line 9, in <module> raise Exception('exception on else') Exception: exception on "else"
If an exception occurs within an except or else clause, the finally clause is also executed before the exception is re-raised.
 

6.3 break, continue, and return statements

def input_number(): x = 0 try: x = int(input('Please enter a number: ')) finally: return x value = input_number() print(f'The number you entered is {value}.')
page icon
Please enter a number: 3 The number you entered is 3.
page icon
Please enter a number: asdf The number you entered is 0.
If a break, continue, or return statement is executed inside a finally clause, the exception is not re-raised after the finally clause.
In the example above, when "asdf" was entered, a ValueError should have been raised, but because the return statement was executed in the finally clause, the exception was not re-raised.
 
def input_number(): x = 0 try: x = int(input('Please enter a number: ')) print('Input successful.') return x finally: print('finally clause') value = input_number() print(f'The number you entered is {value}.')
page icon
Please enter a number: 3 Input successful. finally clause The number you entered is 3.
page icon
Please enter a number: asdf finally clause Traceback (most recent call last): File "path/to/folder/code.py", line 10, in <module> value = input_number() File "path/to/folder/code.py", line 4, in input_number x = int(input('Please enter a number: ')) ~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ValueError: invalid literal for int() with base 10: 'asdf'
In contrast, if a break, continue, or return statement is executed inside a try block, the finally clause is executed first, and then the break, continue, or return statement is executed.
When a break, continue, or return statement is used inside a try block, the ValueError is re-raised as usual.
 
def bool_return(): try: return True finally: return False bool_return()
page icon
False
If a finally clause contains a return statement, the function will always return the value from the finally clause's return statement.
This happens because the finally clause is executed before the return statement inside the try block, even if there is one.
 

7. with statement

with open("myfile.txt") as f: for line in f: print(line, end="")
The with statement is a syntax that simplifies the cleanup of objects, and it is a condensed form of the tryexceptfinally structure.
If an object has __enter__ and __exit__ methods implemented, the with statement can be used.
Then, the with statement will execute the __enter__ method before executing the code, and it will execute the __exit__ method as a finally clause.

8. Handling Multiple Exceptions

8.1 ExceptionGroup

def multiple_exceptions(): excs = [ OSError('error1'), ValueError('error2'), TypeError('error3'), ] raise ExceptionGroup('Multiple exceptions occurred', excs) multiple_exceptions()
page icon
+ Exception Group Traceback (most recent call last): | File "path/to/folder/code.py", line 9, in <module> | multiple_exceptions() | ~~~~~~~~~~~~~~~~~~~^^ | File "path/to/folder/code.py", line 7, in multiple_exceptions | raise ExceptionGroup('Multiple exceptions occurred', excs) | ExceptionGroup: Multiple exceptions occurred (3 sub-exceptions) +-+---------------- 1 ---------------- | OSError: error1 +---------------- 2 ---------------- | ValueError: error2 +---------------- 3 ---------------- | TypeError: error3 +------------------------------------
When you want to raise multiple exceptions, you can use the ExceptionGroup class to raise multiple exceptions at once.
This can be used for unrelated exceptions, such as when multiple exceptions occur during parallel processing.
 

8.2 except* statement

def multiple_exceptions(): excs = [ OSError('error1'), ValueError('error2'), TypeError('error3'), ] raise ExceptionGroup('Multiple exceptions occurred', excs) try: multiple_exceptions() except* OSError as e: print(f'Caught OSError: {e.args}') except* ValueError as e: print(f'Caught ValueError: {e.args}') except* TypeError as e: print(f'Caught TypeError: {e.args}')
page icon
Caught OSError: ('Multiple exceptions occurred', [OSError('error1')]) Caught ValueError: ('Multiple exceptions occurred', [ValueError('error2')]) Caught TypeError: ('Multiple exceptions occurred', [TypeError('error3')])
When multiple exceptions are raised at once using ExceptionGroup, you can use the except* statement instead of the except statement to match the individual exceptions within the ExceptionGroup.
 

9. add_note function

try: raise TypeError('bad type') except Exception as e: e.add_note('Add some information') e.add_note('Add some more information') raise
page icon
Traceback (most recent call last): File "path/to/folder/code.py", line 2, in <module> raise TypeError('bad type') TypeError: bad type Add some information Add some more information
When re-raising an exception received in an except block using raise, you can use the add_note(note) function to attach additional information to the error message as a note.
The add_note function is a method of the Exception class and adds a note to the __notes__ of the exception instance.
The __notes__ attribute is not directly visible but is revealed only when the exception is traced back.