Learning Python - Exception Handling & Resource Management
Episode 6 of 23

Learning Python - Exception Handling & Resource Management

This episode covers Python error handling: try, except, else, and finally, plus creating custom exceptions. You'll also learn context managers with the with keyword and the contextlib module for building your own context managers.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

A good program isn't one that's free of errors, but one that handles errors correctly. Episode 6 equips you with exception handling and resource management — two skills that determine the quality of production code.

We'll dissect the try, except, else, and finally blocks, create custom exceptions, use context managers with the with keyword, and use them to manage resources like files and database connections.

try-except-else-finally Blocks

Basic try-except Structure

Exception handling starts with try and except blocks:

PythonDasar try-except
try:
    angka = int("bukan angka")
except ValueError:
    print("terjadi error konversi")

The try: block contains code that might error, and except ValueError catches that specific error. Catching specific exception types is better than blindly catching all errors.

Using else and finally

else runs when there's no error, and finally always runs:

PythonLengkap dengan else dan finally
def bagi(a, b):
    try:
        hasil = a / b
    except ZeroDivisionError:
        print("pembagi tidak boleh nol")
        return None
    else:
        print("hasil:", hasil)
        return hasil
    finally:
        print("selalu dieksekusi")
 
print(bagi(10, 2))
print(bagi(10, 0))

The call bagi(10, 2) triggers the else block because there's no error, while bagi(10, 0) triggers except. The finally block runs in both cases — the ideal place for resource cleanup.

Catching Multiple Exceptions

One except block can catch several types at once:

PythonMulti-except
try:
    data = [1, 2, 3]
    print(data[5])
    hasil = 10 / 0
except (IndexError, ZeroDivisionError) as err:
    print("error:", err)

except (IndexError, ZeroDivisionError) as err catches two error types at once and stores the exception object in err. This prevents writing repetitive except blocks.

Creating Custom Exceptions

Defining Your Own Exceptions

For domain-specific business logic, create custom exceptions by subclassing Exception:

PythonException custom
class SaldoTidakCukup(Exception):
    pass
 
def tarik(saldo, jumlah):
    if jumlah > saldo:
        raise SaldoTidakCukup(f"saldo {saldo} tidak cukup untuk {jumlah}")
    return saldo - jumlah
 
try:
    print(tarik(5000, 10000))
except SaldoTidakCukup as err:
    print("gagal:", err)

class SaldoTidakCukup(Exception) defines a new error type. Use raise SaldoTidakCukup(...) to throw it. Custom exceptions let callers catch domain errors specifically.

Hierarchy and Best Practices

Some exception best practices:

  • Subclass Exception, not BaseException directly.
  • Provide clear, actionable messages.
  • Don't catch an error and stay silent without information.
  • Log errors you can't handle for observability (episode 21).

Context Managers with with

Why You Need with

Resources like files and connections must be closed after use. Context managers with the with keyword handle this automatically:

PythonMembaca file dengan with
with open("contoh.txt", "w") as f:
    f.write("halo python")
 
with open("contoh.txt", "r") as f:
    isi = f.read()
 
print(isi)

with open("contoh.txt", "w") as f: opens a file and guarantees it's closed automatically, even if an error occurs inside the block. Without with, you'd have to call f.close() manually, which is easy to forget.

contextlib and Custom Context Managers

Building Your Own Context Manager

The contextlib module provides tools for creating context managers. The most commonly used is the @contextmanager decorator:

PythonMembuat context manager dengan contextlib
from contextlib import contextmanager
 
@contextmanager
def pengatur_waktu(nama):
    import time
    mulai = time.perf_counter()
    try:
        yield
    finally:
        lama = time.perf_counter() - mulai
        print(f"{nama}: {lama:.4f} detik")
 
with pengatur_waktu("proses"):
    total = sum(range(1_000_000))

@contextmanager turns a generator into a context manager. The code before yield is setup, and the code after yield inside finally is cleanup. with pengatur_waktu("proses"): uses it like a built-in context manager.

Suppress and ExitStack

contextlib also provides practical utilities:

PythonSuppress exception
from contextlib import suppress
 
with suppress(FileNotFoundError):
    isi = open("tidak_ada.txt").read()
    print(isi)
 
print("tidak error")

suppress(FileNotFoundError) suppresses the specified exception without needing a try block. This makes code cleaner when you genuinely want to ignore certain errors. ExitStack is useful for managing many context managers dynamically.

Closing

Key takeaways:

  • try-except catches errors, else runs on success, finally always runs.
  • Catch specific exception types, not all errors.
  • Custom exceptions subclass Exception with clear messages.
  • The with keyword closes resources automatically even on error.
  • contextlib with @contextmanager makes context managers easy.
  • suppress silences specific exceptions with clean code.

In the next episode, episode 7, we'll cover modularization, basic packaging, and virtual environments — creating packages, a minimal setup.cfg or pyproject.toml, pip install -e for development, and the virtual environment workflow. This is the bridge from scripts to structured projects!

Learning Python - Exception Handling & Resource Management | Learn Python