CÔNG CỤ
MODULES
THAM KHẢO
CÁC CHỦ ĐỀ
BÀI MỚI NHẤT
MỚI CẬP NHẬT

Thông báo: Download 4 khóa học Python từ cơ bản đến nâng cao tại đây.

Tìm hiểu về Decorator trong Python

Trong Python, decorator là một công cụ mạnh mẽ giúp mở rộng và thay đổi hành vi của các hàm hoặc phương thức mà không cần sửa đổi mã nguồn ban đầu. Việc hiểu và sử dụng decorator không chỉ giúp bạn viết mã ngắn gọn, dễ bảo trì mà còn tăng cường khả năng tái sử dụng mã. Trong bài viết này, mình sẽ tìm hiểu khái niệm decorator trong Python, tìm hiểu cách chúng hoạt động và cách bạn có thể phát triển các decorator của riêng mình để áp dụng trong các dự án thực tế.

test php

banquyen png
Bài viết này được đăng tại freetuts.net, không được copy dưới mọi hình thức.

Decorator trong Python là gì?

Một decorator là một hàm nhận một hàm khác làm đối số và mở rộng hành vi của hàm đó mà không thay đổi hàm gốc.

Hãy lấy một ví dụ đơn giản để hiểu rõ hơn khái niệm này.

Ví dụ đơn giản về decorator trong Python

Đoạn mã sau định nghĩa hàm net_price:

Bài viết này được đăng tại [free tuts .net]

def net_price(price, tax):
    """ Tính giá ròng từ giá bán và thuế
    Tham số:
        price: giá bán
        tax: thuế giá trị gia tăng hoặc thuế bán hàng
    Trả về:
        giá ròng
    """
    return price * (1 + tax)

Hàm net_price tính giá ròng từ giá bán và thuế. Nó trả về giá ròng dưới dạng một số.

Giả sử bạn cần định dạng giá ròng theo đơn vị tiền tệ USD. Ví dụ, 100 thành $100. Để làm điều này, bạn có thể sử dụng một decorator.

Theo định nghĩa, một decorator là một hàm nhận một hàm làm đối số:

def currency(fn):
    pass

Và nó trả về một hàm khác:

def currency(fn):
    def wrapper(*args, **kwargs):
        fn(*args, **kwargs)
    return wrapper

Hàm currency trả về hàm wrapper. Hàm wrapper có các tham số *args**kwargs.

Các tham số này cho phép bạn gọi bất kỳ hàm fn nào với bất kỳ sự kết hợp nào của các đối số vị trí và đối số từ khóa.

Trong ví dụ này, hàm wrapper về cơ bản thực thi hàm fn trực tiếp và không thay đổi bất kỳ hành vi nào của hàm fn.

Trong hàm wrapper, bạn có thể gọi hàm fn, lấy kết quả của nó và định dạng kết quả dưới dạng chuỗi tiền tệ:

def currency(fn):
    def wrapper(*args, **kwargs):
        result = fn(*args, **kwargs)
        return f'${result}'
    return wrapper

Hàm currency là một decorator.

Nó chấp nhận bất kỳ hàm nào trả về một số và định dạng số đó thành chuỗi tiền tệ.

Để sử dụng decorator currency, bạn cần truyền hàm net_price vào nó để nhận một hàm mới và thực thi hàm mới như thể đó là hàm gốc. Ví dụ:

net_price = currency(net_price)
print(net_price(100, 0.05))

Kết quả:

$105.0

Định nghĩa decorator trong Python

Nói chung, một decorator là:

  • Một hàm nhận một hàm khác (hàm gốc) làm đối số và trả về một hàm khác (hoặc closure).
  • Hàm closure thường chấp nhận bất kỳ sự kết hợp nào của các đối số vị trí và đối số từ khóa.
  • Hàm closure gọi hàm gốc bằng cách sử dụng các đối số được truyền vào closure và trả về kết quả của hàm.

Hàm bên trong là một closure vì nó tham chiếu đến đối số fn từ phạm vi bao quanh của nó hoặc hàm decorator.

Ký hiệu @

Trong ví dụ trước, currency là một decorator. Bạn có thể trang trí hàm net_price bằng cú pháp sau:

net_price = currency(net_price)

Thông thường, nếu decorate là một hàm decorator và bạn muốn trang trí một hàm khác fn, bạn có thể sử dụng cú pháp này:

fn = decorate(fn)

Để tiện lợi hơn, Python cung cấp một cách ngắn gọn như sau:

@decorate
def fn():
    pass

Ví dụ, thay vì sử dụng cú pháp sau:

net_price = currency(net_price)

Bạn có thể trang trí hàm net_price bằng ký hiệu @currency như sau:

@currency
def net_price(price, tax):
    """ Tính giá ròng từ giá bán và thuế
    Tham số:
        price: giá bán
        tax: thuế giá trị gia tăng hoặc thuế bán hàng
    Trả về:
        giá ròng
    """
    return price * (1 + tax)

Đặt tất cả lại với nhau.

def currency(fn):
    def wrapper(*args, **kwargs):
        result = fn(*args, **kwargs)
        return f'${result}'
    return wrapper


@currency
def net_price(price, tax):
    """ Tính giá ròng từ giá bán và thuế
    Tham số:
        price: giá bán
        tax: thuế giá trị gia tăng hoặc thuế bán hàng
    Trả về:
        giá ròng
    """
    return price * (1 + tax)


print(net_price(100, 0.05))

Tìm hiểu hàm được trang trí trong Python

Khi bạn trang trí một hàm:

@decorate
def fn(*args, **kwargs):
    pass

Nó tương đương với:

fn = decorate(fn)

Hàm decorate trả về một hàm mới, đó là hàm wrapper.

Nếu bạn sử dụng hàm tích hợp help để hiển thị tài liệu của hàm mới, bạn sẽ không thấy tài liệu của hàm gốc. Ví dụ:

help(net_price)

Kết quả:

wrapper(*args, **kwargs)
None

Ngoài ra, nếu bạn kiểm tra tên của hàm mới, Python sẽ trả về tên của hàm bên trong được trả về bởi decorator:

print(net_price.__name__)

Kết quả:

wrapper

Vì vậy, khi bạn trang trí một hàm, bạn sẽ mất chữ ký và tài liệu gốc của hàm.

Để khắc phục điều này, bạn có thể sử dụng hàm wraps từ mô-đun tiêu chuẩn functools. Thực tế, hàm wraps cũng là một decorator.

Dưới đây là cách sử dụng decorator wraps:

from functools import wraps


def currency(fn):
    @wraps(fn)
    def wrapper(*args, **kwargs):
        result = fn(*args, **kwargs)
        return f'${result}'
    return wrapper


@currency
def net_price(price, tax):
    """ Tính giá ròng từ giá bán và thuế
    Tham số:
        price: giá bán
        tax: thuế giá trị gia tăng hoặc thuế bán hàng
    Trả về:
        giá ròng
    """
    return price * (1 + tax)


help(net_price)
print(net_price.__name__)

Kết quả:

net_price(price, tax)
    Tính giá ròng từ giá bán và thuế
    Tham số:
        price: giá bán
        tax: thuế giá trị gia tăng hoặc thuế bán hàng
    Trả về:
        giá ròng

net_price

Kết bài

Một decorator là một công cụ hữu ích giúp thay đổi hành vi của một hàm khác mà không cần thay đổi mã nguồn của nó một cách rõ ràng. Sử dụng ký hiệu @ giúp bạn dễ dàng áp dụng decorator cho hàm. Để giữ lại tài liệu và tên của hàm gốc khi sử dụng decorator, bạn nên sử dụng hàm wraps từ mô-đun tích hợp functools. Việc hiểu và áp dụng decorator một cách hiệu quả sẽ giúp bạn viết mã Python linh hoạt, dễ bảo trì và tăng cường khả năng tái sử dụng.

Cùng chuyên mục:

Cách tạo một widget nhập ngày và giờ sử dụng PyQt QDateTimeEdit

Cách tạo một widget nhập ngày và giờ sử dụng PyQt QDateTimeEdit

Cách tạo một widget nhập giờ sử dụng lớp PyQt QTimeEdit

Cách tạo một widget nhập giờ sử dụng lớp PyQt QTimeEdit

Cách tạo một widget nhập ngày sử dụng lớp PyQt QDateEdit

Cách tạo một widget nhập ngày sử dụng lớp PyQt QDateEdit

Cách sử dụng widget PyQt QSpinBox để tạo một spin box

Cách sử dụng widget PyQt QSpinBox để tạo một spin box

Sử dụng PyQt QComboBox để tạo Widget Combobox

Sử dụng PyQt QComboBox để tạo Widget Combobox

Cách sử dụng lớp PyQt QRadioButton

Cách sử dụng lớp PyQt QRadioButton

Cách sử dụng lớp QCheckBox trong PyQt

Cách sử dụng lớp QCheckBox trong PyQt

Cách sử dụng QFormLayout trong PyQt

Cách sử dụng QFormLayout trong PyQt

Cách sử dụng QGridLayout trong PyQt

Cách sử dụng QGridLayout trong PyQt

Cách sử dụng QVBoxLayout trong PyQt

Cách sử dụng QVBoxLayout trong PyQt

Cách sử dụng widget QLineEdit trong PyQt

Cách sử dụng widget QLineEdit trong PyQt

Cách sử dụng widget QPushButton của PyQt

Cách sử dụng widget QPushButton của PyQt

Cách sử dụng widget PyQt QLabel

Cách sử dụng widget PyQt QLabel

Tín hiệu và Khe (Signals & Slots) trong PyQt

Tín hiệu và Khe (Signals & Slots) trong PyQt

PyQt là gì? Tạo một chương trình

PyQt là gì? Tạo một chương trình "Hello World".

Ứng dụng System Tray với Tkinter

Ứng dụng System Tray với Tkinter

Cách hiển thị đồ thị từ thư viện Matplotlib trong Tkinter

Cách hiển thị đồ thị từ thư viện Matplotlib trong Tkinter

Cách sử dụng Validate trong Tkinter

Cách sử dụng Validate trong Tkinter

Cấu trúc MVC trong Tkinter

Cấu trúc MVC trong Tkinter

Cách sử dụng widget PhotoImage của Tkinter

Cách sử dụng widget PhotoImage của Tkinter

Top