Lewati ke konten utama

Menulis Context Manager Kustom di Python

Pelajari aspek lanjutan manajemen sumber daya di Python dengan menguasai cara menulis context manager kustom.
Diperbarui 29 Sep 2026

Jelajahi bersama AI

ChatGPTClaudePerplexity

Motivasi

Anda mungkin sudah familiar dengan pernyataan with, cara ringkas untuk menangani sumber daya di Python. Namun, pernahkah Anda bertanya-tanya bagaimana cara kerjanya di balik layar? Kekayaan dari kata kunci with berasal dari context manager.

Context manager adalah pola desain fundamental di Python yang menyediakan pendekatan terstruktur untuk manajemen sumber daya. Pola ini memastikan sumber daya diakuisisi, digunakan dengan benar, lalu akhirnya dilepas atau dibersihkan, bahkan saat terjadi error atau exception.

Hal ini menjadi sangat penting saat berurusan dengan sumber daya seperti file, koneksi jaringan, atau handle basis data. Dengan menggunakan context manager, kita dapat menulis kode yang lebih bersih dan andal, membebaskan diri dari kekhawatiran lupa menutup file atau melepas lock.

Dalam tutorial ini, kita akan melampaui context manager bawaan Python dan belajar menulis milik kita sendiri.

Memahami Context Manager di Python

Di balik layar, context manager adalah objek yang mendefinisikan dua metode khusus: __enter__ dan __exit__. Metode __enter__ dipanggil saat Anda memasuki blok with, dan nilai kembalinya ditetapkan ke sebuah variabel di dalam blok tersebut. Di sisi lain, metode __exit__ dipanggil saat blok with keluar, terlepas dari apakah selesai normal atau dengan exception.

Struktur ini memastikan penanganan sumber daya yang tepat. Mari lihat beberapa context manager bawaan di Python untuk memahaminya lebih lanjut:

1. Manajemen File

Ambil contoh klasik membuka file:

with open('file.txt', 'r') as file:
   data = file.read()

Di sini, open('file.txt', 'r') bertindak sebagai context manager. Saat Anda memasuki blok with, metode enter dari objek file dipanggil, membuka file dan menugaskannya ke variabel file. Anda kemudian dapat menggunakan file.read() untuk mengakses konten file.

Berikutnya, metode __exit__ dari objek file dijamin dipanggil saat blok with keluar, bahkan jika terjadi exception. Metode ini menangani penutupan file, memastikan Anda tidak meninggalkan handle file yang tetap terbuka.

2. Penguncian Thread

Melebihi file, context manager juga dapat digunakan untuk sinkronisasi thread menggunakan threading.Lock():

import threading

lock = threading.Lock()
with lock:
   # Critical section
   print("This code is executed under lock protection.")

Di sini, lock adalah objek threading.Lock, context manager lainnya. Saat Anda memasuki blok with, metode __enter__ memperoleh lock, memastikan hanya satu thread yang dapat mengeksekusi critical section pada satu waktu. Terakhir, metode __exit__ melepas lock saat keluar dari blok with, memungkinkan thread lain untuk melanjutkan.

3. Koneksi Basis Data

Demikian pula, context manager dapat mengelola koneksi basis data:

import sqlite3

with sqlite3.connect('database.db') as connection:
    cursor = connection.cursor()
    cursor.execute("SELECT * FROM table")
    rows = cursor.fetchall()

Panggilan sqlite3.connect('database.db') adalah context manager. Memasuki blok with membangun koneksi ke basis data, menugaskannya ke connection. Anda kemudian dapat menggunakan cursor untuk berinteraksi dengan basis data. Metode __exit__ menjamin koneksi ditutup saat blok with keluar, mencegah kebocoran sumber daya.

4. Soket Jaringan

Context manager bahkan dapat menangani komunikasi jaringan:

import socket

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect(('localhost', 8080))
    s.sendall(b'Hello, world')
    data = s.recv(1024)

Di sini, socket.socket(socket.AF_INET, socket.SOCK_STREAM) membuat objek soket yang bertindak sebagai context manager. Metode __enter__ membuat soket, dan di dalam blok with, Anda dapat tersambung, mengirim data, dan menerima data. Metode __exit__ memastikan soket ditutup dengan benar saat selesai.

5. Pemindaian Direktori

Kali ini, os.scandir('.') menyediakan cara untuk mengiterasi entri direktori:

with os.scandir('.') as entries:
   for entry in entries:
       print(entry.name)

os.scandir('.') bertindak sebagai context manager di sini. Metode __enter__ membuka pemindaian direktori, dan Anda dapat mengiterasi entri di dalam blok with. Metode __exit__ membersihkan pemindaian direktori saat keluar.

Seperti yang Anda lihat, pernyataan with yang didukung context manager menyederhanakan manajemen sumber daya dengan menangani alokasi dan dealokasi secara otomatis.

Sekarang, mari akhirnya lihat cara menulis manager kustom Anda sendiri untuk mendapatkan kontrol yang lebih rinci atas manajemen sumber daya.

Ada dua pendekatan utama untuk menulis context manager kustom: berbasis kelas dan berbasis fungsi.

Pendekatan Berbasis Kelas

Pendekatan berbasis kelas adalah metode paling terstruktur dan fleksibel untuk menulis context manager. Di sini, Anda mendefinisikan kelas yang mengimplementasikan metode khusus __enter__ dan __exit__.

Mari lihat contoh kelas Timer yang mengukur waktu eksekusi:

import time


class Timer:
   def __enter__(self):
       self.start_time = time.time()
       return self

   def __exit__(self, exc_type, exc_value, traceback):
       self.end_time = time.time()
       elapsed_time = self.end_time - self.start_time
       print(f"Elapsed time: {elapsed_time} seconds")


# Example usage
if __name__ == "__main__":
   with Timer() as timer:
       # Code block to measure the execution time
       time.sleep(2)  # Simulate some time-consuming operation
Elapsed time: 2.002082347869873 seconds

Di sini, kelas Timer mendefinisikan metode __enter__ untuk menangkap waktu mulai saat Anda memasuki blok with. Ia mengembalikan self agar objek dapat diakses di dalam blok. Metode __exit__ menghitung waktu berlalu saat keluar dari blok with dan mencetaknya.

Pendekatan Berbasis Fungsi

Jika Anda lebih menyukai pendekatan yang lebih ringkas meski mungkin mengorbankan fleksibilitas, pendekatan berbasis fungsi bisa menjadi pilihan yang baik. Di sini, Anda menggunakan dekorator contextmanager dari modul contextlib untuk mengonversi fungsi apa pun menjadi context manager.

Mari lihat cara kerjanya:

import time
from contextlib import contextmanager


@contextmanager
def timer():
   start_time = time.time()
   yield
   end_time = time.time()
   elapsed_time = end_time - start_time
   print(f"Elapsed time: {elapsed_time} seconds")


# Example usage
if __name__ == "__main__":
   with timer():
       time.sleep(2)
Elapsed time: 2.0020740032196045 seconds

Dekorator @contextmanager mengubah fungsi timer menjadi context manager. Di dalam fungsi, start_time ditangkap, dan pernyataan yield menjeda eksekusi, memungkinkan kode di dalam blok with dijalankan.

Terakhir, fungsionalitas __exit__ dicapai dengan menangkap waktu akhir dan mencetak waktu berlalu.

Intinya, Anda menulis logika untuk __enter__ sebelum kata kunci yield sedangkan logika untuk __exit__ berada setelahnya. Kedua pendekatan mencapai hasil yang sama, tetapi pilihan bergantung pada preferensi Anda terhadap struktur dan keterbacaan.

Contoh Praktis Context Manager

Sekarang setelah Anda memahami cara menulis context manager kustom di Python, mari lihat beberapa contoh praktis di mana ini sangat berguna.

Mengelola Operasi File

Operasi file adalah tugas umum di banyak aplikasi. Baik membaca maupun menulis ke file, pengelolaan yang tepat memastikan integritas data dan efisiensi sumber daya. Mari pertimbangkan skenario di mana kita ingin menulis teks ke file menggunakan context manager kustom.

class FileManager:
   def __init__(self, filename, mode):
       self.filename = filename
       self.mode = mode
       self.file = None

   def __enter__(self):
       self.file = open(self.filename, self.mode)
       return self.file

   def __exit__(self, exc_type, exc_value, traceback):
       if self.file:
           self.file.close()


# Example usage
if __name__ == "__main__":
   with FileManager("example.txt", "w") as file:
       file.write("Hello, world!\n")

Dalam contoh ini, kita mendefinisikan kelas FileManager untuk meniru fungsi open di Python yang menangani operasi file. Mari uraikan apa yang dilakukan setiap metode:

  • __init__(self, filename, mode): Metode ini adalah konstruktor kelas. Ia menginisialisasi objek FileManager dengan nama file dan mode yang diberikan, yang menentukan file yang akan dibuka dan mode pembukaannya (misalnya, baca, tulis, tambah).
  • __enter__(self): Metode ini dipanggil saat memasuki blok with seperti biasa. Ia membuka file yang ditentukan oleh filename dalam mode yang ditentukan oleh mode. Objek file yang dibuka ditetapkan ke self.file dan dikembalikan untuk digunakan di dalam blok with.
  • __exit__(self, exc_type, exc_value, traceback): Metode ini dipanggil saat keluar dari blok with, terlepas dari apakah terjadi exception di dalam blok. Ia memastikan file ditutup dengan benar dengan memanggil self.file.close() jika self.file bukan None. Ini mencegah kebocoran sumber daya dan memastikan pembersihan yang tepat.

Jika Anda memeriksa isi example.txt setelah mengeksekusi kode di atas, Anda akan melihat teks yang tertulis:

$ cat example.txt
Hello, world!

Tentu saja, keuntungan kelas FileManager adalah Anda dapat mengimplementasikan logika kustom apa pun untuk bekerja dengan file. Misalnya, Anda dapat memodifikasinya agar kelas menyimpan log file mana yang dibuka atau ditutup ke sebuah file teks.

import datetime


class FileManager:
   def __init__(self, filename, mode, log_filename="file_log.txt"):
       self.filename = filename
       self.mode = mode
       self.log_filename = log_filename
       self.file = None

   def log_action(self, action):
       timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
       with open(self.log_filename, "a") as log_file:
           log_file.write(f"{timestamp} - {action}: {self.filename}\n")

   def __enter__(self):
       self.file = open(self.filename, self.mode)
       self.log_action("Opened")
       return self.file

   def __exit__(self, exc_type, exc_value, traceback):
       if self.file:
           self.file.close()
           self.log_action("Closed")


# Example Usage
with FileManager("example.txt", "r") as file:
   content = file.read()
   print(content)

Hello, world!

Dalam versi yang dimodifikasi ini:

  • Modul datetime diimpor untuk bekerja dengan stempel waktu.
  • Metode log_action ditambahkan untuk mencatat aksi file.
  • Parameter log_filename ditambahkan untuk menentukan nama file untuk file log. Nilainya default ke 'file_log.txt'.
  • Nama file dan aksi (dibuka atau ditutup) ditambahkan ke file log menggunakan metode log_action di dalam metode __enter__ dan __exit__.
  • Di dalam metode log_action, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") digunakan untuk mendapatkan stempel waktu saat ini dalam format "YYYY-MM-DD HH:MM:SS".
  • Stempel waktu kemudian disertakan dalam log bersama nama file dan aksinya.

Sekarang, jika kita mencetak isi file_log.txt, kita akan melihat lognya:

$ cat file_log.txt
2024-04-03 12:37:02 - Opened: example.txt
2024-04-03 12:37:02 - Closed: example.txt

Contoh penggunaan praktis lainnya yang dapat kita implementasikan sebagai context manager kustom adalah alat manajemen basis data SQLite:

import sqlite3


class DatabaseConnection:
   def __init__(self, database_name):
       self.database_name = database_name
       self.connection = None

   def __enter__(self):
       self.connection = sqlite3.connect(self.database_name)
       return self.connection

   def __exit__(self, exc_type, exc_value, traceback):
       if self.connection:
           self.connection.close()

Sekali lagi, ini meniru fungsi sqlite3.connect, tetapi dapat ditingkatkan lebih lanjut dengan menambahkan logika manajemen basis data kustom. Mari periksa apakah manager bekerja dengan beberapa fungsi contoh:

# Example usage
def create_table():
   with DatabaseConnection("example.db") as connection:
       cursor = connection.cursor()
       cursor.execute(
           """CREATE TABLE IF NOT EXISTS users (
                           id INTEGER PRIMARY KEY,
                           username TEXT,
                           email TEXT)"""
       )


def insert_data(username, email):
   with DatabaseConnection("example.db") as connection:
       cursor = connection.cursor()
       cursor.execute(
           "INSERT INTO users (username, email) VALUES (?, ?)", (username, email)
       )
       connection.commit()


def fetch_data():
   with DatabaseConnection("example.db") as connection:
       cursor = connection.cursor()
       cursor.execute("SELECT * FROM users")
       return cursor.fetchall()


if __name__ == "__main__":
   create_table()
   insert_data("john_doe", "john@example.com")
   insert_data("jane_doe", "jane@example.com")
   users = fetch_data()
   print("Users in the database:")
   for user in users:
       print(user)

Users in the database:
(1, 'john_doe', 'john@example.com')
(2, 'jane_doe', 'jane@example.com')
(3, 'john_doe', 'john@example.com')
(4, 'jane_doe', 'jane@example.com')

Kita dapat menulis kode yang jauh lebih mudah dipelihara dengan memecah operasi basis data menjadi fungsi-fungsi dan menggunakan manager kustom yang baru saja kita buat.

Topik Lanjutan Terkait Context Manager di Python

Di bagian ini, kita akan membahas topik lanjutan terkait context manager di Python. Ini mencakup penanganan error dan nesting context manager.

Penanganan error

Penanganan error adalah aspek penting pemrograman, dan context manager di Python menyediakan cara yang nyaman untuk mengelola error dalam operasi manajemen sumber daya.

Dalam contoh kelas FileManager, penanganan error dapat diimplementasikan di dalam metode __exit__ untuk memastikan pembersihan yang tepat bahkan jika terjadi exception. Misalnya, jika terjadi error saat menulis data ke file (ketika berada di dalam blok with), kita tetap ingin memastikan file ditutup untuk mencegah kebocoran sumber daya. Kita dapat memodifikasi metode __exit__ sebagai berikut:

def __exit__(self, exc_type, exc_value, traceback):
   if self.file:
       self.file.close()
       if exc_type is not None:
           print(f"An error occurred: {exc_value}")

Di sini, kita terlebih dahulu menutup file menggunakan self.file.close() untuk memastikan pembersihan yang tepat. Lalu, kita periksa apakah terjadi exception (exc_type is not None). Jika ya, kita mencetak pesan error untuk memberikan umpan balik tentang apa yang salah.

Berikut ini:

  • exc_type adalah kelas exception, seperti AttributeError dan IndexError.
  • exc_value adalah instance exception dengan pesannya.
  • traceback adalah traceback lengkap dari error.

Mari jalankan kelas FileManager dengan kode yang sengaja salah:

with FileManager("example.txt", "r") as file:
   # Simulate a sample error
   content = file.read()
   print(content[150])

An error occurred: string index out of range

Jadi, untuk menerapkan logika kustom saat menangani exception di manager kustom Anda, berikut alur contoh yang dapat Anda ikuti:

def __exit__(self, exc_type, exc_value, traceback):
   # Perform cleanup
   ...

   # Handle the exception
   if isinstance(exc_value, AttributeError):
       # Handle AttributeError here
       print(f"Exception message: {exc_value}")
   # Handle another exception
   elif isinstance(exc_value, AnotherError):
       ...

Menyusun (nesting) context manager

Menyusun context manager memungkinkan Anda mengelola beberapa sumber daya secara bersamaan, masing-masing dengan context manager-nya sendiri, dalam satu pernyataan with. Ini sangat berguna saat menangani operasi kompleks yang melibatkan banyak sumber daya. Mari lihat bagaimana kita dapat menyusun context manager dalam contoh kita.

Pertimbangkan skenario di mana kita ingin membuka file dan melakukan operasi basis data dalam konteks yang sama. Mungkin, kita ingin memasukkan konten file CSV sebagai baris ke dalam basis data. Kita dapat mencapainya dengan menyusun context manager FileManager dan DatabaseConnection dalam satu pernyataan with:

if __name__ == "__main__":
   with FileManager("example.txt", "w") as file, DatabaseConnection("example.db") as connection:
       ...

Dalam contoh ini, context manager FileManager dan DatabaseConnection disusun dalam pernyataan with yang sama. Ini memungkinkan kita untuk membuka file dan membangun koneksi basis data secara bersamaan. Operasi penulisan file dan basis data selanjutnya dilakukan dalam konteks gabungan ini.

Satu hal yang perlu diingat adalah keterbacaan kode. Saya menyarankan jumlah konteks simultan tidak melebihi dua.

Kesimpulan

Context manager di Python menawarkan pendekatan terstruktur untuk manajemen sumber daya di dalam pernyataan with, memastikan alokasi dan dealokasi sumber daya yang tepat.

Sepanjang tutorial ini, kita telah mempelajari dasar-dasar context manager, meninjau contoh praktis, dan membahas topik lanjutan seperti penanganan error dan penyusunan. Dengan menguasai penulisan context manager kustom, Anda dapat menulis kode yang lebih bersih dan andal, meningkatkan penanganan error, dan mengelola sumber daya secara efisien.

Topik penulisan context manager kustom berada di bawah payung kategori “Advanced Python”. Berikut beberapa sumber terkait untuk meningkatkan keterampilan Anda:


Bexruz (Bex) Tuychiev's photo
Author
Bexruz (Bex) Tuychiev
LinkedIn

Saya adalah kreator konten data science dengan pengalaman lebih dari 2 tahun dan salah satu dengan jumlah pengikut terbesar di Medium. Saya suka menulis artikel mendalam tentang AI dan ML dengan sedikit gaya sarkastik—karena harus ada sesuatu untuk membuatnya tidak terlalu membosankan. Saya telah menghasilkan lebih dari 130 artikel dan satu kursus di DataCamp, dengan satu lagi sedang dalam proses. Konten saya telah dilihat oleh lebih dari 5 juta pasang mata, dan 20 ribu di antaranya menjadi pengikut di Medium dan LinkedIn. 

Topik
Python

Lanjutkan Perjalanan Python Anda Hari Ini!

Program

Pemrograman Python

19 jam
Tingkatkan keterampilan pemrograman Anda. Pelajari cara mengoptimalkan kode, menulis fungsi dan tes, serta menggunakan teknik rekayasa perangkat lunak terbaik.
Lihat DetailRight Arrow
Mulai Kursus
Lihat SelengkapnyaRight Arrow
Terkait

blogs

40 Pertanyaan Wawancara DBMS Teratas di 2026

Kuasai pertanyaan wawancara basis data, dari konsep SQL dasar hingga skenario desain sistem tingkat lanjut. Panduan mendalam ini mencakup semua yang Anda perlukan untuk sukses di wawancara DBMS dan meraih peran berikutnya.
Dario Radečić's photo

Dario Radečić

15 mnt

blogs

Tutorial Korelasi di R

Dapatkan pengenalan dasar-dasar korelasi di R: pelajari lebih lanjut tentang koefisien korelasi, matriks korelasi, plotting korelasi, dan sebagainya.
David Woods's photo

David Woods

13 mnt

blogs

12 Alternatif ChatGPT Terbaik yang Bisa Anda Coba pada 2026

Artikel ini menyajikan daftar alternatif ChatGPT yang akan meningkatkan produktivitas Anda.
Javier Canales Luna's photo

Javier Canales Luna

14 mnt

blogs

Spaghetti Plot dan Jalur Badai

Temukan alasan mengapa Anda sebaiknya (tidak) menggunakan spaghetti plot untuk menyampaikan ketidakpastian jalur prediksi badai serta dampaknya terhadap interpretasi.
Hugo Bowne-Anderson's photo

Hugo Bowne-Anderson

13 mnt

Lihat SelengkapnyaLihat Selengkapnya