2.23. Decorator¶
Một decorator là một cú pháp bao bọc một hàm (hoặc một phương thức) trong một hàm khác. Hàm bao có thể thêm hành vi trước và sau lần gọi gốc, thay thế giá trị trả về, hoặc gắn metadata. Cú pháp là:
@wrapper
def f():
...
Dòng @wrapper tương đương với f = wrapper(f) -- hàm f được xây dựng bình thường, sau đó được chuyển cho wrapper, và kết quả được gán lại cho tên f.
Một decorator nhận một hàm vào và trả về một hàm mới.¶
2.23.1. Các decorator phương thức có sẵn¶
Một số decorator đi kèm với Python và được sử dụng bên trong thân lớp.
2.23.1.1. @property¶
Biến một phương thức thành một thuộc tính tính toán. Người gọi truy cập nó như thể là một thuộc tính thông thường (không có dấu ngoặc đơn), nhưng một phương thức chạy mỗi lần đọc:
class Circle:
def __init__(self, radius):
self.radius = radius
@property
def area(self):
return 3.14159 * self.radius * self.radius
c = Circle(5)
print(c.area) # 78.53975 -- no parentheses
Hãy sử dụng khi một thuộc tính trông đơn giản nhưng cần một phép tính nhỏ ở phía sau. Nếu công việc tốn kém, hãy ưu tiên dùng phương thức thông thường -- người gọi không mong đợi việc đọc thuộc tính sẽ chậm.
2.23.1.2. @classmethod¶
Định nghĩa một phương thức nhận lớp làm đối số đầu tiên thay vì một thể hiện. Tham số đầu tiên thường được đặt tên là cls:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
@classmethod
def origin(cls):
return cls(0, 0)
p = Point.origin()
Các phương thức lớp là cách tiêu chuẩn để cung cấp các hàm tạo thay thế -- các hàm nhà máy trả về một thể hiện được xây dựng theo cách không mặc định.
2.23.1.3. @staticmethod¶
Định nghĩa một phương thức không nhận thể hiện cũng không nhận lớp -- đó chỉ là một hàm thông thường nằm trong không gian tên lớp vì lý do tổ chức:
class Temperature:
@staticmethod
def c_to_f(c):
return c * 9 / 5 + 32
Temperature.c_to_f(100) # 212.0
Hãy dùng ít thôi; nếu một hàm thực sự không liên quan đến trạng thái của lớp, thì một hàm cấp mô-đun thông thường thường gọn gàng hơn.
2.23.2. Viết một decorator tùy chỉnh¶
Một decorator là một hàm nhận một hàm và trả về một hàm. Cú pháp tối thiểu:
def log_calls(func):
def wrapper(*args, **kwargs):
print("calling", func.__name__)
return func(*args, **kwargs)
return wrapper
@log_calls
def add(a, b):
return a + b
add(2, 3)
Đầu ra:
calling add
Hàm wrapper đóng gói func và chuyển tiếp mọi thứ cho nó. *args / **kwargs cho phép nó hoạt động trên bất kỳ hàm nào, không chỉ các hàm có hai đối số. Mẫu này là nền tảng của các decorator phức tạp hơn (đo thời gian, lưu bộ nhớ đệm, thử lại khi thất bại) nhưng cốt lõi luôn giống nhau: nhận một hàm vào, trả về một hàm ra.