In Python, try
and except
are used to handle exceptions. Additionally, else
and finally
can be used to define actions to take at the end of the try-except process.
- 8. Errors and Exceptions - Handling Exceptions — Python 3.11.3 documentation
- 8. Compound statements - The try statement — Python 3.11.3 documentation
Contents
- Basic exception handling in Python: try ... except ...
- Catch a specific exception
- Store the exception object
- Work with base classes
- Flow when an exception occurs
- Catch multiple exceptions
- Apply different operations to multiple exceptions
- Apply the same operation to multiple exceptions
- Catch all exceptions
- Wildcard except (Bare except)
- Base class: Exception
- Execute action if no exception occurs: try ... except ... else ...
- Clean-up action: try ... except ... finally ...
- Ignore exceptions: pass
- Practical example: Reading image files
- Without exception handling
- With exception handling
You can also set up debugging assertions with the assert
statement.
- The assert statement in Python
Basic exception handling in Python: try ... except ...
Catch a specific exception
For example, when attempting division by zero, a ZeroDivisionError
is raised, causing the process to end.
# print(1 / 0)# ZeroDivisionError: division by zero
source: exception_handling.py
To catch this exception, use try
and except
as follows:
try: print(1 / 0)except ZeroDivisionError: print('Error')# Error
source: exception_handling.py
Store the exception object
By using except <exception-name> as <variable-name>:
, the exception object is stored in the variable. You can choose any name for the variable, but names like e
and err
are commonly used.
The exception object contains error messages that are displayed when an exception occurs, allowing you to check the error details by outputting them.
try: print(1 / 0)except ZeroDivisionError as e: print(e) print(type(e))# division by zero# <class 'ZeroDivisionError'>
source: exception_handling.py
In Python 2, it should be written as except <exception-name>, <variable-name>:
.
Work with base classes
You can also specify a base class. For example, ArithmeticError
is the base class for ZeroDivisionError
. The variable stores the exception object of the derived class that actually occurred.
print(issubclass(ZeroDivisionError, ArithmeticError))# Truetry: print(1 / 0)except ArithmeticError as e: print(e) print(type(e))# division by zero# <class 'ZeroDivisionError'>
source: exception_handling.py
Check the official documentation for built-in exceptions in Python.
Flow when an exception occurs
When an exception occurs in the try
clause, the subsequent code in the try
clause is skipped.
For example, if an exception occurs in the middle of the for
loop, the loop ends at that point, and the code in the except
clause is executed.
try: for i in [-2, -1, 0, 1, 2]: print(1 / i)except ZeroDivisionError as e: print(e)# -0.5# -1.0# division by zero
source: exception_handling.py
You can specify the code to execute after the except
clause using the else
and finally
clauses, which will be described later.
Catch multiple exceptions
Define the following function that catches ZeroDivisionError
.
def divide(a, b): try: print(a / b) except ZeroDivisionError as e: print('catch ZeroDivisionError:', e)
source: exception_handling.py
This function can catch ZeroDivisionError
, but it cannot catch other exceptions.
divide(1, 0)# catch ZeroDivisionError: division by zero# divide('a', 'b')# TypeError: unsupported operand type(s) for /: 'str' and 'str'
source: exception_handling.py
Apply different operations to multiple exceptions
You can use multiple except
clauses to handle different exceptions with different operations.
def divide_each(a, b): try: print(a / b) except ZeroDivisionError as e: print('catch ZeroDivisionError:', e) except TypeError as e: print('catch TypeError:', e)divide_each(1, 0)# catch ZeroDivisionError: division by zerodivide_each('a', 'b')# catch TypeError: unsupported operand type(s) for /: 'str' and 'str'
source: exception_handling.py
Apply the same operation to multiple exceptions
You can specify multiple exception names in a single except
clause by using a tuple
.
def divide_same(a, b): try: print(a / b) except (ZeroDivisionError, TypeError) as e: print(e)divide_same(1, 0)# division by zerodivide_same('a', 'b')# unsupported operand type(s) for /: 'str' and 'str'
source: exception_handling.py
Catch all exceptions
You can also catch all exceptions without specifying them.
Wildcard except (Bare except)
All exceptions can be caught by omitting the exception name from the except
clause. If there are multiple except
clauses, the exception name can be omitted only in the last except
clause.
The use of an except
clause without specifying the exception name is referred to as a wildcard except or bare except, and as mentioned in the official documentation, caution should be exercised when using it.
The last except clause may omit the exception name(s), to serve as a wildcard. Use this with extreme caution, since it is easy to mask a real programming error in this way! 8. Errors and Exceptions - Handling Exceptions — Python 3.9.0 documentation
def divide_wildcard(a, b): try: print(a / b) except: print('Error')divide_wildcard(1, 0)# Errordivide_wildcard('a', 'b')# Error
source: exception_handling.py
Using a wildcard except, you can catch all exceptions, including SystemExit
(raised by sys.exit()
, etc.) and KeyboardInterrupt
(triggered by pressing Ctrl + C
). However, it's often preferable not to catch these particular exceptions. In such cases, using Exception
instead may be a better option, as described next.
Base class: Exception
You can specify Exception
in the except
clause, which is the base class for all built-in, non-system-exiting exceptions.
def divide_exception(a, b): try: print(a / b) except Exception as e: print(e)divide_exception(1, 0)# division by zerodivide_exception('a', 'b')# unsupported operand type(s) for /: 'str' and 'str'
source: exception_handling.py
The class hierarchy for built-in exceptions is as follows.
BaseException ├── BaseExceptionGroup ├── GeneratorExit ├── KeyboardInterrupt ├── SystemExit └── Exception ├── StopAsyncIteration ├── StopIteration ├── ... ...
Since SystemExit
and KeyboardInterrupt
do not inherit from Exception
, using Exception
in the except
clause will not catch sys.exit()
or interrupts like Ctrl + C
.
- Built-in Exceptions - SystemExit — Python 3.11.3 documentation
- Built-in Exceptions - KeyboardInterrupt — Python 3.11.3 documentation
The base class for all built-in exceptions, including SystemExit
and KeyboardInterrupt
, is BaseException
. If you specify BaseException
instead of Exception
in the except
clause, all exceptions will be caught.
It is better to specify the expected exceptions in the except
clause as much as possible, because catching an unexpected exception may lead to a bug.
Execute action if no exception occurs: try ... except ... else ...
You can use the else
clause to specify an action to be executed if no exception occurs. If an exception does occur and is caught by an except
clause, the action in the else
clause will not be executed.
def divide_else(a, b): try: print(a / b) except ZeroDivisionError as e: print('catch ZeroDivisionError:', e) else: print('finish (no error)')divide_else(1, 2)# 0.5# finish (no error)divide_else(1, 0)# catch ZeroDivisionError: division by zero
source: exception_handling.py
Clean-up action: try ... except ... finally ...
In the finally
clause, you can specify the clean-up action to be executed whether an exception occurs or not.
def divide_finally(a, b): try: print(a / b) except ZeroDivisionError as e: print('catch ZeroDivisionError:', e) finally: print('all finish')divide_finally(1, 2)# 0.5# all finishdivide_finally(1, 0)# catch ZeroDivisionError: division by zero# all finish
source: exception_handling.py
You can also use the else
and finally
clause together. If no exception occurs, the else
clause is executed and then the finally
clause is executed.
def divide_else_finally(a, b): try: print(a / b) except ZeroDivisionError as e: print('catch ZeroDivisionError:', e) else: print('finish (no error)') finally: print('all finish')divide_else_finally(1, 2)# 0.5# finish (no error)# all finishdivide_else_finally(1, 0)# catch ZeroDivisionError: division by zero# all finish
source: exception_handling.py
Ignore exceptions: pass
If you want to catch an exception and continue without taking any action, use pass
.
def divide_pass(a, b): try: print(a / b) except ZeroDivisionError: passdivide_pass(1, 0)
source: exception_handling.py
See the following article for details on the pass
statement.
- The pass statement in Python
Practical example: Reading image files
A convenient example of using exception handling is reading image files.
The following is an example of resizing image files in a folder using Pillow.
- Resize images with Python, Pillow
Without exception handling
Get all file paths in the folder with glob()
and resize only files that match specific extensions.
- How to use glob() in Python
- Get the filename, directory, extension from a path string in Python
- The in operator in Python (for list, string, dictionary, etc.)
import osimport globfrom PIL import Imagedst_dir = 'data/temp/images_half'os.makedirs(dst_dir, exist_ok=True)
source: pillow_image_resize_all.py
files = glob.glob('./data/temp/images/*')for f in files: root, ext = os.path.splitext(f) if ext in ['.jpg', '.png']: img = Image.open(f) img_resize = img.resize((img.width // 2, img.height // 2)) basename = os.path.basename(root) img_resize.save(os.path.join(dst_dir, basename + '_half' + ext))
source: pillow_image_resize_all.py
Since image files can have various extensions, it is difficult to specify all of them.
With exception handling
files = glob.glob('./data/temp/images/*')for f in files: try: img = Image.open(f) img_resize = img.resize((img.width // 2, img.height // 2)) root, ext = os.path.splitext(f) basename = os.path.basename(root) img_resize.save(os.path.join(dst_dir, basename + '_half' + ext)) except OSError as e: pass
source: pillow_image_resize_all.py
All files that can be opened with Pillow's Image.open()
are resized.
The approach that explicitly checks the conditions, like in the first example, is called "LBYL: Look Before You Leap", while the approach that uses exception handling, like in the second example, is called "EAFP: Easier to Ask for Forgiveness than Permission".
- Duck typing with hasattr() and abstract base class in Python
Both approaches have pros and cons, but using exception handling can make the code more concise when dealing with processes that involve many conditions.