Nhảy đến nội dung chính

Decorator – Gói thêm chức năng cho hàm

Decorator là một khái niệm cực kỳ mạnh mẽ và phổ biến trong Python – đặc biệt khi bạn muốn gói thêm (wrap) chức năng cho một hàm mà không thay đổi mã gốc.

Decorator là một hàm dùng để gói thêm logic cho một hàm khác.

Bạn có thể hình dung nó giống như việc “bọc thêm lớp áo” cho một người – người đó vẫn là người đó, nhưng có thêm tính năng mới (ví dụ: áo giáp, áo tàng hình,... ).

I. Cấu trúc cơ bản

def my_decorator(func):
    def wrapper(*args, **kwargs):
        # Thêm chức năng trước khi gọi func
        print("Bắt đầu gọi hàm...")
        result = func(*args, **kwargs)
        # Thêm chức năng sau khi gọi func
        print("Đã gọi xong.")
        return result
    return wrapper

Dùng như sau:

@my_decorator
def say_hello():
    print("Xin chào!")

say_hello()

Kết quả

Bắt đầu gọi hàm...
Xin chào!
Đã gọi xong.

II. Ví dụ thực tế

1. Log thời gian thực thi của một hàm

import time

def timer(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"Hàm {func.__name__} chạy trong {end - start:.2f} giây")
        return result
    return wrapper

@timer
def slow_function():
    time.sleep(2)
    print("Xong việc rồi")

slow_function()

2. Kiểm tra phân quyền trước khi gọi hàm (giống middleware)

def require_admin(func):
    def wrapper(user, *args, **kwargs):
        if user != 'admin':
            print("Không có quyền truy cập")
            return None
        return func(user, *args, **kwargs)
    return wrapper

@require_admin
def view_dashboard(user):
    print(f" Welcome {user}, đây là dashboard.")

view_dashboard("guest")   #  Không có quyền truy cập
view_dashboard("admin")   #  Welcome admin, đây là dashboard.

III. Decorator có tham số (Decorator Factory)

Ví dụ bạn muốn tạo decorator có thể truyền tham số:

def repeat(n):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(n):
                func(*args, **kwargs)
        return wrapper
    return decorator

@repeat(3)
def hello():
    print("Hi!")

hello()
Hi!
Hi!
Hi!

IV. Giữ metadata của hàm gốc (functools.wraps)

Mặc định khi bạn dùng decorator, thông tin gốc của hàm như tên và docstring sẽ bị mất.

from functools import wraps

def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("Bọc hàm nè")
        return func(*args, **kwargs)
    return wrapper

V. Decorator trong Django và thực tế

Tên Decorator Chức năng
@login_required Đảm bảo người dùng đã đăng nhập trước khi truy cập view
@require_POST Chỉ cho phép method POST (dùng trong view)
@csrf_exempt Bỏ qua kiểm tra CSRF (thường dùng khi làm API)
@transaction.atomic Gói các thao tác DB trong một transaction an toàn
from django.contrib.auth.decorators import login_required

@login_required
def dashboard(request):
    return render(request, 'dashboard.html')

VI. Tóm lại

Khái niệm Giải thích ngắn
Decorator Hàm gói hàm khác, thêm chức năng mà không sửa hàm gốc
Dùng @decorator_name Cú pháp gọn gàng để áp dụng
functools.wraps() Giữ tên, docstring, v.v. của hàm gốc
Có thể lồng nhau Decorator A gói decorator B

Tác giả: Đỗ Ngọc Tú
Công Ty Phần Mềm VHTSoft