Python Decorator dan Context Manager: Encapsulasi Logic dengan Elegan
Decorator adalah pola desain yang memungkinkan kita memodifikasi perilaku function tanpa mengubah source code aslinya. Context manager menyediakan mekanisme untuk mengelola resource seperti file atau koneksi database dengan pattern setup dan teardown yang terjamin. Kedua fitur ini membantu kita menulis code yang lebih bersih, reusable, dan mudah dimaintain. Mereka merepresentasikan first-class functions dan duck typing philosophy yang membuat Python expressive dan powerful.
Decorator Adalah Function Wrapper
Decorator pada dasarnya adalah function yang menerima function lain sebagai argument dan mengembalikan function baru. Syntax @ di Python menyediakan cara elegan untuk menerapkan decorator.
def uppercase_decorator(func):
def wrapper():
result = func()
return result.upper()
return wrapper
@uppercase_decorator
def say_hello():
return "hello world"
print(say_hello())Output:
HELLO WORLDFunction uppercase_decorator menerima say_hello sebagai argument. wrapper function mengeksekusi function asli dan memodifikasi return value sebelum dikembalikan. Syntax @ secara otomatis melewati function yang didekorasi ke decorator. Hasilnya adalah function baru dengan perilaku yang diperluas tanpa modifikasi definisi asli. Kita juga dapat menumpuk multiple decorator pada satu function dengan menuliskannya secara berurutan dari atas ke bawah.
Membuat Decorator untuk Mengukur Execution Time
Salah satu use case paling umum untuk decorator adalah profiling performance. Kita dapat membungkus function dengan timing logic yang reusable.
import time
from functools import wraps
def timer_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
end = time.perf_counter()
print(f"{func.__name__} executed in {end - start:.4f}s")
return result
return wrapper
@timer_decorator
def slow_function():
time.sleep(0.5)
return "done"
@timer_decorator
def compute_sum(n):
return sum(range(n))
slow_function()
compute_sum(1000000)Output:
slow_function executed in 0.5001s
compute_sum executed in 0.0203sDecorator timer_decorator menggunakan @wraps dari functools untuk mempertahankan metadata function asli. Tanpa @wraps, attribute seperti __name__ dan __doc__ akan tertimpa oleh wrapper. Decorator ini menerima args dan *kwargs sehingga kompatibel dengan function apa pun. Hasil timing dicetak ke console dan function tetap mengembalikan nilai aslinya. Pattern ini sering kita temui di web framework dan data pipeline untuk monitoring bottleneck.
Implementasi Retry Logic dengan Decorator
Decorator juga efektif untuk mengimplementasikan cross-cutting concerns seperti retry mechanism. Pattern ini berguna saat kita berinteraksi dengan service eksternal yang tidak stabil.
import time
from functools import wraps
def retry(max_attempts=3, delay=1):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(1, max_attempts + 1):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"Attempt {attempt} failed: {e}")
if attempt == max_attempts:
raise
time.sleep(delay)
return None
return wrapper
return decorator
@retry(max_attempts=3, delay=0.5)
def unstable_function():
import random
if random.random() < 0.7:
raise RuntimeError("Service unavailable")
return "success"
result = unstable_function()
print(result)Output:
Attempt 1 failed: Service unavailable
Attempt 2 failed: Service unavailable
successDecorator retry menerima parameter sehingga kita dapat mengkonfigurasi max_attempts dan delay. Pattern ini disebut decorator factory. Function wrapper menangkap exception dan mencoba eksekusi ulang hingga batas attempt tercapai. Jika semua attempt gagal, exception terakhir akan di-raise kembali ke caller. Pattern ini sangat berguna untuk HTTP requests, database connections, atau API calls yang mengalami transient failures.
Data Science with Python
Master the art of data analysis, visualization, and predictive modeling.
Context Manager Mengelola Resource Secara Aman
Context manager memastikan resource dibuka dan ditutup dengan benar. Statement with di Python menjamin cleanup code dijalankan meskipun terjadi exception di tengah proses.
class DatabaseConnection:
def __init__(self, db_name):
self.db_name = db_name
self.connected = False
def __enter__(self):
print(f"Connecting to {self.db_name}...")
self.connected = True
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print(f"Closing connection to {self.db_name}...")
self.connected = False
return False
def query(self, sql):
if not self.connected:
raise RuntimeError("Not connected")
return f"Result for: {sql}"
with DatabaseConnection("production_db") as db:
result = db.query("SELECT * FROM users")
print(result)
print(f"Connected after with block: {db.connected}")Output:
Connecting to production_db...
Result for: SELECT * FROM users
Closing connection to production_db...
Connected after with block: FalseMethod __enter__ dijalankan saat konteks dimulai. Method __exit__ dijalankan saat blok with selesai. Parameter exc_type, exc_val, dan exc_tb menyimpan informasi exception jika terjadi error. Return value False dari __exit__ membiarkan exception propagate ke caller. Return value True akan men-swallow exception. Protocol ini dikenal sebagai context management protocol dan menjadi dasar dari statement with di Python.
Menyederhanakan Context Manager dengan contextlib
Modul contextlib menyediakan decorator contextmanager yang mengubah generator function menjadi context manager. Pendekatan ini menghasilkan code yang lebih ringkas dibandingkan class-based approach.
from contextlib import contextmanager
@contextmanager
def managed_file(filename, mode):
file = open(filename, mode)
try:
yield file
finally:
file.close()
with managed_file("output.txt", "w") as f:
f.write("Hello from context manager")
with managed_file("output.txt", "r") as f:
content = f.read()
print(content)Output:
Hello from context managerDecorator @contextmanager menghasilkan context manager dari generator function. Statement yield memisahkan setup code dan teardown code. Code sebelum yield adalah setup, code setelah yield (biasanya dalam blok finally) adalah teardown. Pattern ini ideal untuk resource management yang sederhana dan menghindari boilerplate class. Mayoritas context manager di codebase modern menggunakan pendekatan ini karena lebih readable.
Context Manager Bawaan yang Sering Digunakan
Python menyediakan beberapa context manager bawaan yang powerful. Modul contextlib memiliki utilitas tambahan selain @contextmanager.
from contextlib import suppress, redirect_stdout
import io
# suppress: ignore specific exception
with suppress(FileNotFoundError):
with open("nonexistent.txt", "r") as f:
content = f.read()
# redirect_stdout: capture print output
buffer = io.StringIO()
with redirect_stdout(buffer):
print("This goes to buffer")
print("Not to console")
print("Captured:", buffer.getvalue())Output:
Captured: This goes to buffer
Not to consolesuppress secara elegan mengabaikan exception tertentu tanpa blok try-except yang verbose. redirect_stdout berguna untuk testing function yang mencetak output ke console. Kedua utilitas ini menunjukkan bagaimana context manager dapat menyederhanakan code yang biasanya memerlukan boilerplate ekstensif.
Menggabungkan Decorator dan Context Manager dalam Satu Workflow
Kedua pola ini sering digunakan bersama dalam codebase production. Decorator meng-encapsulasi behavior yang reusable, sedangkan context manager menangani lifecycle resource.
from contextlib import contextmanager
from functools import wraps
import time
def log_execution(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"Starting {func.__name__}...")
result = func(*args, **kwargs)
print(f"Finished {func.__name__}")
return result
return wrapper
@contextmanager
def timed_context(label):
start = time.perf_counter()
yield
elapsed = time.perf_counter() - start
print(f"{label} took {elapsed:.4f}s")
@log_execution
def process_data():
with timed_context("data processing"):
time.sleep(0.3)
print("Processing complete")
process_data()Output:
Starting process_data...
Processing complete
data processing took 0.3001s
Finished process_dataDecorator log_execution membungkus function dengan log messages. Context manager timed_context mengukur waktu eksekusi blok spesifik. Kombinasi ini menghasilkan code yang modular dan mudah ditrace tanpa menambah complexity ke business logic utama. Kita dapat menambah atau menghapus concern tanpa mengubah implementasi function inti. Separation of concerns menjadi lebih jelas dan maintenance lebih mudah dilakukan.
Kapan Menggunakan Decorator atau Context Manager
Gunakan decorator ketika kita perlu menambahkan behavior secara transversal ke multiple functions. Timing, logging, caching, dan autentikasi adalah contoh kasus yang ideal untuk decorator. Decorator memungkinkan kita mengaplikasikan concern yang sama ke banyak function dengan satu baris syntax. Mereka mendukung separation of concerns dengan cara yang Pythonic.
Gunakan context manager ketika kita perlu mengelola state dan resource dengan jaminan cleanup. File I/O, database transactions, dan lock acquisition adalah scenario yang cocok untuk pattern ini. Context manager memastikan resource tidak leak meskipun terjadi exception. Cleanup code selalu dijalankan sehingga program tetap stabil.
Kedua fitur ini merupakan fundamental Python yang sering muncul di framework populer seperti Flask, Django, dan SQLAlchemy. Memahami cara kerja internal decorator dan context manager membantu kita membaca dan menulis library Python dengan lebih percaya diri. Investasi waktu untuk memahami kedua pattern ini akan mempercepat workflow development secara signifikan.
Mau menguasai pattern Python seperti decorator dan context manager secara praktis? Bergabunglah dengan Python Bootcamp di Rumah Coding. Kurikulum berbasis project dengan bimbingan mentor profesional.
Course Terkait
Data Science with Python
Master the art of data analysis, visualization, and predictive modeling.
E-commerce Sales Dashboard
- Data Cleaning Pipeline
- Interactive Charts
- Sales Forecasting Model
Deep Learning Bootcamp
A beginner-friendly, highly interactive bootcamp designed to take you from foundational concepts to deploying real-world Artificial Intelligence applications. Through a completely project-based approach, you will master the core of Deep Learning, Artificial Neural Networks, and Computer Vision using Python and TensorFlow, ultimately building a professional-grade AI web application for your portfolio.
GreenGuard: Intelligent Plant Disease Diagnosis Web App
- Interactive Image Upload UI: A clean, user-friendly interface built with Streamlit that supports drag-and-drop image uploads directly from a computer or mobile phone.
- Real-Time AI Inference: Utilizes a lightweight, optimized CNN model (like MobileNetV2) to process the image and return a diagnosis in seconds without heavy server load.
- Confidence Scoring Dashboard: Visually displays the model's prediction probability (e.g., "95% confident this is Tomato Late Blight") using interactive progress bars or charts.
LLM Bootcamp
This project-based bootcamp is designed for beginners to dive practically into the world of Large Language Models (LLMs). Through hands-on building, you will learn how to interact with top-tier AI APIs, master prompt engineering, orchestrate complex workflows using LangChain, and implement Retrieval-Augmented Generation (RAG) to query your own documents. By the end of this course, you will have the skills to build, test, and deploy a fully functional, custom AI web application.
Domain-Specific AI Knowledge Assistant
- Dynamic Document Processing: A sidebar interface allowing users to upload new PDF or TXT files, which the app automatically chunks, embeds, and stores in the vector database.
- Context-Aware Chat UI: A modern chat interface built with Streamlit that maintains conversation history, allowing users to ask follow-up questions naturally.
- Strict Guardrails (Anti-Hallucination): System instructions designed so the AI politely declines to answer questions that fall outside the context of the uploaded documents.