Decorator Có Tham Số Và Xếp Chồng (Stacking) Trong Python: Nâng Tầm Kỹ Năng Lập Trình Nâng Cao

Khi bạn đã thành thạo việc viết các Decorator cơ bản, bước tiếp theo để trở thành một "bậc thầy" Python là làm chủ Decorator có tham số (Decorators with Arguments) và kỹ thuật Xếp chồng nhiều Decorator (Stacking Decorators). Những kỹ thuật này giúp mã nguồn của bạn trở nên linh hoạt, mô-đun hóa cao và cực kỳ mạnh mẽ trong các hệ thống lớn.
Bài viết này sẽ dẫn dắt bạn đi từ cấu trúc 3 tầng phức tạp cho đến quy tắc xếp chồng chuẩn chỉnh trong Python.
🟩 PHẦN 1: DECORATOR CÓ THAM SỐ (DECORATORS WITH ARGUMENTS)
1. Cấu trúc 3 tầng (Triple-Nested Structure)
Khác với Decorator thông thường (chỉ có 2 tầng), khi bạn muốn truyền riêng tham số cấu hình vào cho Decorator (Ví dụ: @retry(max_attempts=3)), bạn bắt buộc phải xây dựng cấu trúc 3 tầng hàm lồng nhau:
- Tầng 1 (Hàm ngoài cùng): Nhận tham số cấu hình của riêng Decorator.
- Tầng 2 (Hàm ở giữa): Nhận hàm gốc (func) làm đầu vào.
- Tầng 3 (Hàm trong cùng): Hàm wrapper(*args, **kwargs) chứa logic thực thi chính.
from functools import wraps
def retry(max_attempts: int):
# Tầng 1: Nhận tham số cấu hình cho Decorator
print(f"-> Khởi tạo Decorator retry với số lần thử tối đa: {max_attempts}")
def decorator_retry(original_function):
# Tầng 2: Nhận hàm gốc
@wraps(original_function)
def wrapper(*args, **kwargs):
# Tầng 3: Xử lý logic thực thi (tận dụng biến max_attempts từ Tầng 1 nhờ Closure)
attempts = 0
while attempts < max_attempts:
try:
print(f"-> Lần thử thứ {attempts + 1}...")
return original_function(*args, **kwargs)
except Exception as e:
attempts += 1
if attempts >= max_attempts:
raise e
print(f"-> Thất bại, đang thử lại...")
return wrapper # Kết thúc Tầng 2 bằng lệnh return wrapper
return decorator_retry # Kết thúc Tầng 1 bằng lệnh trả về hàm ở Tầng 2
2. Vận dụng Closure và Cú pháp thực thi
- Closure: Nhờ cơ chế đóng gói phạm vi, hàm wrapper (Tầng 3) hoàn toàn có thể truy cập và sử dụng linh hoạt các biến cấu hình từ Tầng 1 (như max_attempts).
- Cú pháp thực thi: Khi sử dụng, bạn luôn luôn phải có dấu ngoặc tròn (), kể cả khi các tham số của decorator đã có giá trị mặc định:
@retry(max_attempts=3)
def connect_to_database():
# Logic kết nối database có thể lỗi
pass
🟨 PHẦN 2: CHỒNG DECORATOR (STACKING DECORATORS)
Khi một hàm cần áp dụng nhiều tính năng bổ trợ cùng lúc (Ví dụ: Vừa đo thời gian, vừa ghi log, vừa bắt lỗi), bạn có thể xếp chồng nhiều Decorator lên nhau. Tuy nhiên, cần nắm vững 3 quy tắc vàng sau:
1. Thứ tự thực thi (Execution Order)
Quy tắc xếp chồng trong Python là: Các decorator chạy từ dưới lên trên (decorator nào nằm gần hàm gốc nhất sẽ chạy trước, decorator ở xa chạy sau).
@outer_decorator # Chạy thứ 2 (Xa hàm gốc hơn)
@inner_decorator # Chạy đầu tiên (Gần hàm gốc nhất)
def my_function():
pass
2. Tính toàn vẹn dữ liệu kiểu trả về
Đảm bảo kiểu dữ liệu đầu ra (Return type) của decorator phía dưới phải khớp với kiểu dữ liệu đầu vào mong đợi của decorator phía trên. Nếu decorator dưới trả về một cấu trúc dữ liệu lạ mà decorator trên không xử lý được, chương trình sẽ phát sinh lỗi logic hoặc TypeError.
3. Bảo toàn danh tính đồng loạt với @wraps
Tất cả các decorator trong chuỗi xếp chồng bắt buộc đều phải sử dụng @wraps(original_function). Nếu thiếu dù chỉ một decorator ở giữa, tên hàm gốc (__name__) và tài liệu hướng dẫn (__doc__) sẽ bị ghi đè và làm mất tích.
Ví dụ hoàn chỉnh khi xếp chồng hai Decorator chuẩn Production:
from functools import wraps
import time
def logger_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"[LOG] Bắt đầu gọi hàm: {func.__name__}")
result = func(*args, **kwargs)
print(f"[LOG] Hoàn tất hàm: {func.__name__}")
return result
return wrapper
def timing_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"[TIME] Hàm chạy mất: {end_time - start_time:.4f} giây")
return result
return wrapper
# Xếp chồng: timing_decorator chạy trước, sau đó đến logger_decorator
@logger_decorator
@timing_decorator
def calculate_sum(n: int) -> int:
"""Hàm tính tổng dãy số."""
return sum(range(n))
# Sử dụng:
calculate_sum(1000000)
📌 Lời Kết
Việc thành thạo Decorator có tham số (với cấu trúc 3 tầng) và kỹ thuật Xếp chồng (Stacking) sẽ giúp bạn tự do xây dựng các middleware, cơ chế bảo mật, bộ lọc dữ liệu hoặc công cụ tối ưu cực kỳ chuyên nghiệp trong các ứng dụng Python quy mô lớn.
Từ khóa tìm hiểu thêm: Python Decorators with Arguments, Stacking Decorators Python, Triple-Nested Decorator, Python Middleware Pattern.
Bình luận