Decorator được sử dụng tương đối nhiều trong Python.
Decorator là một hàm nhận tham số đầu vào là một hàm khác và mở rộng tính năng cho hàm đó mà không thay đổi nội dung của nó.
Đây cũng được gọi là metaprogramming – siêu lập trình, hiểu đơn giản là “Code sinh ra code”, nghĩa là mình viết một chương trình và chương trình này sẽ sinh ra, điều khiển các chương trình khác hoặc làm một phần công việc ngay tại thời điểm biên dịch.
Điều kiện để có Decorator
Để hiểu về decorator, trước tiên bạn cần xem lại một vài điều cơ bản trong Python.
Hàm là một khái niệm rất cơ bản trong lập trình nói chung. Tuy nhiên, trong Python, hàm cũng là đối tượng. Các tên hàm mà chúng ta khai báo chỉ đơn giản là định danh ràng buộc với các đối tượng này. Một đối tượng hàm cũng có thể được liên kết cùng với nhiều tên khác nhau. Ví dụ:
def first(msg): print(msg) first("Hello") second = first second("Hello")
Khi bạn chạy code, hàm first
và second
đều trả về cùng một output
. Ở đây, first
và second
đề cập đến cùng một đối tượng hàm.
Hãy theo dõi tiếp, các hàm có thể được truyền dưới dạng tham số cho một hàm khác (tương tự như map
, filter
và reduce
trong Python).
Những hàm lấy hàm khác làm tham số đầu vào được gọi là hàm bậc cao (higher-order functions). Ví dụ như này:
def inc(x): return x + 1 def dec(x): return x - 1 def operate(func, x): result = func(x) return result
Chúng ta gọi hàm như sau:
>>> operate(inc,3) 4 >>> operate(dec,3) 2
Hơn nữa, một hàm có thể trả về kết quả một hàm khác.
def is_called(): def is_returned(): print("Hello") return is_returned new = is_called() #Outputs "Hello" new()
Ở đây, is_returned() là một hàm lồng nhau, hàm này sẽ được truy cập và trả về kết quả mỗi khi ta gọi hàm is_called().
Để rõ hơn, bạn có thể tham khảo thêm về sử dụng Closure trong Python
Quay trở lại với Decorator, hiểu một cách cơ bản nhất, Decorator là một hàm có thể nhận các hàm khác, cho phép bạn chạy một số đoạn code trước hoặc sau hàm chính mà không thay đổi kết quả.
def make_pretty(func): def inner(): print("I got decorated") func() return inner def ordinary(): print("I am ordinary")
Chạy code trong Python shell:
>>> ordinary() I am ordinary >>> # Thử hàm decorate trong hàm ordinary >>> pretty = make_pretty(ordinary) >>> pretty() I got decorated I am ordinary
Trong ví dụ trên, make_pretty() là một decorator. Khi ta gọi
pretty = make_pretty(ordinary)
thì hàm ordinary() được decorator truyền vào làm tham số và hàm trả về tên là pretty.
Bạn có thể thấy ở đây, decorator đã thêm một hàm mới cho hàm ban đầu. Hãy hình dung nó như kiểu đóng gói một món quá. Các decorator là lớp bọc ở ngoài, bản chất của đối tượng được decorator truyền vào làm tham số (món quà bên trong) không thay đổi, nhưng hiện giờ nó có thêm một lớp bọc decorator ở ngoài.
Nói chung, ở đây ta decorator một hàm và gán lại nó:
ordinary = make_pretty(ordinary)
Đây là một cấu trúc phổ biến, vì vậy Python có một cú pháp để đơn giản hóa việc này.
Bạn có thể sử dụng ký hiệu @
cùng với tên của hàm decorator và đặt nó lên trên định nghĩa của hàm được decorator. Ví dụ:
@make_pretty def ordinary(): print("I am ordinary")
tương đương với:
def ordinary(): print("I am ordinary") ordinary = make_pretty(ordinary)
Đây là một cú pháp đặc biệt để thực hiện decorator.