Decorators trong Python
Category: Python
Trong Python , decorator là một cách mạnh mẽ và linh hoạt để sửa đổi hoặc mở rộng hành vi của các hàm hoặc phương thức, mà không cần thay đổi mã thực tế của chúng. Decorator về cơ bản là một hàm lấy một hàm khác làm đối số và trả về một hàm mới có chức năng nâng cao.
Trình trang trí thường được sử dụng trong các tình huống như ghi nhật ký, xác thực và ghi nhớ, cho phép chúng ta thêm chức năng bổ sung vào các hàm hoặc phương thức hiện có theo cách sạch sẽ và có thể tái sử dụng.
Ví dụ về Decorator:
# Một hàm decorator đơn giản
def decorator(func):
def wrapper():
print("Before calling the function.")
func()
print("After calling the function.")
return wrapper
# Áp dụng decorator cho một hàm
@decorator
def greet():
print("Hello, World!")
greet()
Đầu ra:
Before calling the function.
Hello, World!
After calling the function.
Giải thích:
trình trang trí lấy hàm chào làm đối số.
Nó trả về một hàm mới (wrapper) đầu tiên in một thông báo, gọi greet() và sau đó in một thông báo khác.
Cú pháp @decorator là cách viết tắt của greet = decorator(greet).
Chúng ta hãy cùng tìm hiểu chi tiết về trình trang trí:
Cú pháp của tham số trang trí
def decorator_name(func):
def wrapper(*args, **kwargs):
# Thêm chức năng trước lệnh gọi hàm gốc
result = func(*args, **kwargs)
# Thêm chức năng sau lệnh gọi hàm gốc
return result
return wrapper
@decorator_name
def function_to_decorate():
# Mã hàm gốc
pass
Giải thích các tham số
1. tên_trang_trí(hàm):
decorator_name: Đây là tên của hàm trang trí.
func: Tham số này biểu diễn hàm được trang trí. Khi bạn sử dụng trình trang trí, hàm được trang trí sẽ được truyền cho tham số này.
2. wrapper(*args, **kwargs):
wrapper: Đây là một hàm lồng nhau bên trong decorator. Nó bao bọc hàm gốc, thêm chức năng bổ sung.
*args: Thu thập mọi đối số theo vị trí được truyền cho hàm được trang trí thành một tuple.
**kwargs: Thu thập mọi đối số từ khóa được truyền cho hàm được trang trí vào trong một từ điển.
Hàm bao bọc cho phép trình trang trí xử lý các hàm với bất kỳ số lượng và kiểu đối số nào.
3. @tên người trang trí:
Cú pháp này áp dụng trình trang trí cho hàm function_to_decorate . Nó tương đương với việc viết function_to_decorate = decorator_name(function_to_decorate).
Các hàm bậc cao
Trong Python, các hàm bậc cao là các hàm lấy một hoặc nhiều hàm làm đối số, trả về một hàm như một kết quả hoặc thực hiện cả hai. Về cơ bản, một hàm bậc cao là một hàm hoạt động trên các hàm khác. Đây là một khái niệm mạnh mẽ trong lập trình hàm và là một thành phần chính để hiểu cách thức hoạt động của trình trang trí.
Các tính chất chính của hàm bậc cao:
Lấy hàm làm đối số : Hàm bậc cao có thể chấp nhận các hàm khác làm tham số.
Trả về hàm : Một hàm bậc cao có thể trả về một hàm mới có thể được gọi sau.
Ví dụ về hàm bậc cao:
# Một hàm bậc cao nhận một hàm khác làm đối số
def fun(f, x):
return f(x)
# Một hàm đơn giản để truyền vào
def square(x):
return x * x
# Sử dụng hàm apply_function để áp dụng hàm bình phương
res = fun(square, 5)
print(res)
Đầu ra
25
Trong ví dụ này, hàm đầu tiên fun là hàm bậc cao vì nó lấy một hàm khác f làm đối số và áp dụng vào giá trị x.
Vai trò của người trang trí:
Decorator trong Python là một loại hàm bậc cao vì chúng lấy một hàm làm đầu vào, sửa đổi hàm đó và trả về một hàm mới mở rộng hoặc thay đổi hành vi của hàm đó. Hiểu về các hàm bậc cao là điều cần thiết để làm việc với decorator vì decorator về cơ bản là các hàm trả về các hàm khác.
Chức năng như Đối tượng hạng nhất
Trong Python, hàm là đối tượng hạng nhất , nghĩa là chúng có thể được xử lý như bất kỳ đối tượng nào khác, chẳng hạn như số nguyên, chuỗi hoặc danh sách. Điều này mang lại cho hàm mức độ linh hoạt độc đáo và cho phép chúng được truyền xung quanh và thao tác theo những cách không thể thực hiện được trong nhiều ngôn ngữ lập trình khác.
Hàm là đối tượng hạng nhất có nghĩa là gì?
Có thể gán cho biến : Hàm có thể được gán cho biến và sử dụng giống như bất kỳ giá trị nào khác.
Có thể được truyền dưới dạng đối số : Hàm có thể được truyền dưới dạng đối số cho các hàm khác.
Có thể trả về từ các hàm khác : Các hàm có thể trả về các hàm khác, đây là một khái niệm quan trọng trong trình trang trí.
Có thể lưu trữ trong các cấu trúc dữ liệu : Các hàm có thể được lưu trữ trong danh sách, từ điển hoặc các cấu trúc dữ liệu khác.
# Gán một hàm cho một biến
def greet(n):
return f"Hello, {n}!"
say_hi = greet # Gán hàm greet cho biến say_hi
print(say_hi("Alice")) # Output: Hello, Alice!
# Truyền một hàm làm đối số
def apply(f, v):
return f(v)
res = apply(say_hi, "Bob")
print(res) # Output: Hello, Bob!
# Trả về một hàm từ một hàm khác
def make_mult(f):
def mult(x):
return x * f
return mult
dbl = make_mult(2)
print(dbl(5)) # Output: 10
Đầu ra:
Hello, Alice!
Hello, Bob!
10
Giải thích:
Mã này định nghĩa hàm chào trả về tin nhắn chào mừng.
Hàm greet được gán cho biến say_hi, được dùng để in lời chào cho "Alice".
Một hàm khác, apply, lấy một hàm và một giá trị làm đối số, áp dụng hàm cho giá trị và trả về kết quả.
apply được chứng minh bằng cách truyền say_hi và "Bob", in ra lời chào cho "Bob".
Hàm make_mult tạo ra một hàm nhân dựa trên một hệ số cho trước.
Vai trò của các hàm hạng nhất trong Decorator
Trình trang trí nhận hàm cần trang trí làm đối số. Điều này cho phép trình trang trí sửa đổi hoặc cải thiện hành vi của hàm.
Trình trang trí trả về một hàm mới bao bọc hàm gốc. Hàm mới này thêm hành vi bổ sung trước hoặc sau khi hàm gốc được gọi.
Khi một hàm được trang trí, nó được gán cho tên biến của hàm gốc. Điều này có nghĩa là hàm gốc được thay thế bằng hàm được trang trí (gói).
Các loại trang trí
1. Trình trang trí hàm:
Kiểu trang trí phổ biến nhất, lấy một hàm làm đầu vào và trả về một hàm mới. Ví dụ trên minh họa kiểu này.
def simple_decorator(func):
def wrapper():
print("Before calling the function.")
func()
print("After calling the function.")
return wrapper
@simple_decorator
def greet():
print("Hello, World!")
greet()
Đầu ra:
Before calling the function.
Hello, World!
After calling the function.
Giải thích:
simple_decorator(func): Trình trang trí này lấy hàm hello làm đối số (func) và trả về một hàm mới (wrapper) bổ sung một số chức năng trước và sau khi gọi hàm ban đầu.
@simple_decorator: Đây là cú pháp trang trí. Nó áp dụng simple_decorator cho hàm greet.
Gọi greet(): Khi greet() được gọi, nó không chỉ thực thi hàm gốc mà còn chạy hành vi được thêm vào từ hàm bao bọc.
2. Phương thức trang trí:
Được sử dụng để trang trí các phương thức trong một lớp. Chúng thường xử lý các trường hợp đặc biệt, chẳng hạn như self đối số cho các phương thức ví dụ.
def method_decorator(func):
def wrapper(self, *args, **kwargs):
print("Before method execution")
res = func(self, *args, **kwargs)
print("After method execution")
return res
return wrapper
class MyClass:
@method_decorator
def say_hello(self):
print("Hello!")
obj = MyClass()
obj.say_hello()
Đầu ra:
Before method execution
Hello!
After method execution
Giải thích:
method_decorator(func): Decorator lấy phương thức (say_hello) làm đối số (func). Nó trả về một hàm bao bọc thêm hành vi trước và sau khi gọi phương thức gốc.
wrapper(self, args, *kwargs): Wrapper phải chấp nhận self vì đây là phương thức của một thể hiện. self là thể hiện của lớp và args và *kwargs cho phép truyền các đối số khác nếu cần.
@method_decorator: Phương thức này áp dụng method_decorator cho phương thức say_hello của MyClass.
Gọi obj.say_hello(): Phương thức say_hello hiện được gói gọn bằng hành vi bổ sung.
3. Trình trang trí lớp
Class decorator được sử dụng để sửa đổi hoặc nâng cao hành vi của một lớp. Giống như function decorator, class decorator được áp dụng cho định nghĩa lớp. Chúng hoạt động bằng cách lấy lớp làm đối số và trả về phiên bản đã sửa đổi của lớp.
Ví dụ:
def fun(cls):
cls.class_name = cls.__name__
return cls
@fun
class Person:
pass
print(Person.class_name)
Đầu ra:
Person
Giải thích:
add_class_name(cls): Bộ trang trí này thêm một thuộc tính mới, class_name, vào lớp cls. Giá trị của class_name được đặt thành tên của lớp (cls.__name__).
@add_class_name: Điều này áp dụng trình trang trí add_class_name cho lớp Person.
Kết quả: Khi lớp Person được định nghĩa, trình trang trí sẽ tự động thêm thuộc tính class_name vào lớp đó.
print(Person.class_name): Truy cập thuộc tính class_name được thêm vào bởi trình trang trí sẽ in ra tên của lớp, Person.
Các Decorator tích hợp phổ biến trong Python
Python cung cấp một số trình trang trí tích hợp thường được sử dụng trong định nghĩa lớp. Các trình trang trí này sửa đổi hành vi của các phương thức và thuộc tính trong một lớp, giúp quản lý và sử dụng chúng hiệu quả hơn. Các trình trang trí tích hợp được sử dụng thường xuyên nhất là @staticmethod, @classmethod, và @property.
@staticmethod
Trình trang trí được sử dụng để định nghĩa một phương thức không hoạt động trên một thể hiện của lớp (tức là không sử dụng ). Các phương thức tĩnh được gọi trên chính lớp đó, không phải trên một thể hiện của lớp @staticmethodself.
Ví dụ:
class MathOperations:
@staticmethod # Định nghĩa một phương thức tĩnh
def add(x, y):
return x + y # Trả về tổng của x và y
# Sử dụng phương thức tĩnh
res = MathOperations.add(5, 3) # Gọi phương thức add mà không cần tạo đối tượng
print(res) # In kết quả: 8
Đầu ra:
8
Giải thích:
add là một phương thức tĩnh được định nghĩa bằng trình trang trí @staticmethod.
Có thể gọi trực tiếp trên lớp MathOperations mà không cần tạo một thể hiện.
@phương pháp lớp
Decorator @classmethod được sử dụng để định nghĩa một phương thức hoạt động trên chính lớp đó (tức là sử dụng cls). Các phương thức lớp có thể truy cập và sửa đổi trạng thái lớp áp dụng cho tất cả các phiên bản của lớp.
Ví dụ:
class Employee:
raise_amount = 1.05
def __init__(self, name, salary):
self.name = name
self.salary = salary
@classmethod
def set_raise_amount(cls, amount):
cls.raise_amount = amount
# Sử dụng phương thức lớp
Employee.set_raise_amount(1.10)
print(Employee.raise_amount)
Đầu ra
1.1
Giải thích:
set_raise_amount là một phương thức lớp được định nghĩa bằng trình trang trí @classmethod.
Nó có thể sửa đổi biến lớp raise_amount cho lớp Employee và tất cả các thể hiện của lớp này.
@tài sản
Decorator @property được sử dụng để định nghĩa một phương thức như một thuộc tính, cho phép bạn truy cập phương thức đó như một thuộc tính. Điều này hữu ích để đóng gói việc triển khai phương thức trong khi vẫn cung cấp một giao diện đơn giản.
Ví dụ:
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if value >= 0:
self._radius = value
else:
raise ValueError("Radius cannot be negative")
@property
def area(self):
return 3.14159 * (self._radius ** 2)
# Sử dụng thuộc tính
c = Circle(5)
print(c.radius)
print(c.area)
c.radius = 10
print(c.area)
Đầu ra:
5
78.53975
314.159
Giải thích:
bán kính và diện tích là các thuộc tính được xác định bằng trình trang trí @property.
Thuộc tính bán kính cũng có phương thức thiết lập để cho phép sửa đổi bằng xác thực.
Các thuộc tính này cung cấp một cách để truy cập và sửa đổi các thuộc tính riêng tư trong khi vẫn duy trì tính đóng gói.
Chuỗi trang trí
Nói một cách đơn giản hơn, việc nối các decorator có nghĩa là trang trí một hàm bằng nhiều decorator.
Ví dụ:
# mã kiêm tra chuỗi
def decor1(func):
def inner():
x = func()
return x * x
return inner
def decor(func):
def inner():
x = func()
return 2 * x
return inner
@decor1
@decor
def num():
return 10
@decor
@decor1
def num2():
return 10
print(num())
print(num2())
Đầu ra
400
200
Published on Jun 18, 2025