Trình trang trí có lẽ thuộc về khả năng thiết kế đẹp nhất và mạnh mẽ nhất trong Python, nhưng đồng thời, khái niệm này được nhiều người coi là phức tạp để tiếp cận. Nói một cách chính xác, việc sử dụng các bộ trang trí rất dễ dàng, nhưng việc viết các bộ trang trí có thể phức tạp, đặc biệt nếu bạn chưa có kinh nghiệm với các bộ trang trí và một số khái niệm lập trình chức năng
Mặc dù có cùng một khái niệm cơ bản, nhưng chúng ta có hai loại trình trang trí khác nhau trong Python
- chức năng trang trí
- Trang trí lớp học
Trình trang trí trong Python là bất kỳ đối tượng Python có thể gọi nào được sử dụng để sửa đổi hàm hoặc lớp. Một tham chiếu đến hàm "func" hoặc lớp "C" được chuyển đến trình trang trí và trình trang trí trả về hàm hoặc lớp đã sửa đổi. Các chức năng hoặc lớp được sửa đổi thường chứa các cuộc gọi đến chức năng ban đầu "func" hoặc lớp "C"
Bạn cũng có thể tham khảo chương của chúng tôi về ghi nhớ với trang trí
Nếu bạn thích hình ảnh ở phía bên phải của trang này và nếu bạn cũng quan tâm đến việc xử lý hình ảnh bằng Python, Numpy, Scipy và Matplotlib, thì chắc chắn bạn sẽ thích chương của chúng tôi về Kỹ thuật xử lý hình ảnh, nó giải thích toàn bộ quá trình tạo-
Đào tạo Python trực tiếp
Thưởng thức trang này?
Thấy. Tổng quan về các khóa học Python trực tiếp
đăng ký tại đây
Những bước đầu tiên để trang trí
Chúng tôi biết từ các lớp đào tạo Python khác nhau của mình rằng có một số điểm trong định nghĩa về trình trang trí khiến nhiều người mới bắt đầu gặp khó khăn
Do đó, chúng tôi sẽ giới thiệu các bộ trang trí bằng cách lặp lại một số khía cạnh quan trọng của chức năng. Trước tiên, bạn phải biết hoặc nhớ rằng tên hàm là tham chiếu đến hàm và chúng ta có thể gán nhiều tên cho cùng một hàm
def succ[x]: return x + 1 successor = succ successor[10]
ĐẦU RA
11
succ[10]
ĐẦU RA
11
Điều này có nghĩa là chúng tôi có hai tên, tôi. e. "succ" và "successor" cho cùng chức năng. Thực tế quan trọng tiếp theo là chúng ta có thể xóa "succ" hoặc "successor" mà không xóa chính chức năng đó
del succ successor[10]
ĐẦU RA
11
Các chức năng bên trong Chức năng
Khái niệm có hoặc định nghĩa hàm bên trong hàm là hoàn toàn mới đối với lập trình viên C hoặc C++
def f[]: def g[]: print["Hi, it's me 'g'"] print["Thanks for calling me"] print["This is the function 'f'"] print["I am calling 'g' now:"] g[] f[]
ĐẦU RA
This is the function 'f' I am calling 'g' now: Hi, it's me 'g' Thanks for calling me
Một ví dụ khác sử dụng các câu lệnh trả về "thích hợp" trong các hàm
________số 8_______
ĐẦU RA
It's 68.0 degrees!
Ví dụ sau đây là về hàm giai thừa, mà trước đây chúng ta đã định nghĩa như sau
110
Điều gì xảy ra nếu ai đó chuyển một giá trị âm hoặc số float cho hàm này? . Bạn có thể có ý tưởng để kiểm tra như sau
111
Ví dụ, nếu bạn gọi hàm này bằng
def temperature[t]: def celsius2fahrenheit[x]: return 9 * x / 5 + 32 result = "It's " + str[celsius2fahrenheit[t]] + " degrees!" return result print[temperature[20]]8, tôi. e.
def temperature[t]: def celsius2fahrenheit[x]: return 9 * x / 5 + 32 result = "It's " + str[celsius2fahrenheit[t]] + " degrees!" return result print[temperature[20]]9, điều đầu tiên được kiểm tra là liệu đó có phải là số nguyên dương của tôi không. Về nguyên tắc, điều này có ý nghĩa. "Sự cố" hiện xuất hiện trong bước đệ quy. Bây giờ
It's 68.0 degrees!0 được gọi. Cuộc gọi này và tất cả những cuộc gọi khác cũng kiểm tra xem đó có phải là số nguyên dương hay không. Nhưng điều này là không cần thiết. Nếu bạn trừ đi giá trị
It's 68.0 degrees!1 từ một số nguyên dương, bạn sẽ nhận được một số nguyên dương hoặc lại là
It's 68.0 degrees!2. Vì vậy, cả hai giá trị đối số được xác định rõ ràng cho chức năng của chúng tôi
Với một hàm lồng nhau [hàm cục bộ], người ta có thể giải quyết vấn đề này một cách tao nhã
112
Chúng ta có thể mở rộng miền giá trị đầu vào có thể có cho hàm
It's 68.0 degrees!3 của mình bằng cách cho phép các số float tương đương với số nguyên, i. e. thỏa mãn điều kiện int[x] == x. Nếu chúng ta biết rằng một biến x tham chiếu đến một giá trị float, chúng ta cũng có thể sử dụng phép thử x. is_integer[]
Việc triển khai giai thừa sau đây tuân theo phân tích trường hợp chi tiết hơn về đối số như đã thảo luận trước đây
113
Hãy để chúng tôi kiểm tra chức năng trước đó
114
ĐẦU RA
115
Chức năng như tham số
Nếu bạn chỉ nhìn vào các ví dụ trước, điều này dường như không hữu ích lắm. Nó trở nên hữu ích khi kết hợp với hai khả năng mạnh mẽ hơn nữa của các hàm Python. Do thực tế là mọi tham số của một hàm là một tham chiếu đến một đối tượng và các hàm cũng là các đối tượng, chúng ta có thể chuyển các hàm - hay tốt hơn là "các tham chiếu đến các hàm" - làm tham số cho một hàm. Chúng tôi sẽ chứng minh điều này trong ví dụ đơn giản tiếp theo
116
ĐẦU RA
117
Bạn có thể không hài lòng với đầu ra. 'f' nên viết rằng nó gọi là 'g' chứ không phải 'func'. Tất nhiên, chúng ta cần biết tên 'thật' của func là gì. Với mục đích này, chúng ta có thể sử dụng thuộc tính
It's 68.0 degrees!4, vì nó chứa tên này
118
ĐẦU RA
119
Đầu ra giải thích những gì đang xảy ra một lần nữa. Một vi dụ khac
succ[10]0
ĐẦU RA
succ[10]1
Hàm trả về Hàm
Đầu ra của một chức năng cũng là một tham chiếu đến một đối tượng. Do đó, các hàm có thể trả về các tham chiếu đến các đối tượng hàm
succ[10]2
ĐẦU RA
succ[10]3
Ví dụ trước trông rất giả tạo và hoàn toàn vô dụng. Bây giờ chúng tôi sẽ trình bày một ví dụ định hướng ngôn ngữ khác, cho thấy một liên lạc thực tế hơn. Được rồi, vẫn không phải là một chức năng hữu ích theo cách của nó. Chúng tôi viết một chức năng với tên gần như tự giải thích
It's 68.0 degrees!5. Vì vậy, hàm này trả về [hoặc tạo] các hàm có thể được sử dụng để tạo người ở các ngôn ngữ khác nhau, tôi. e. Đức, Pháp, Ý, Thổ Nhĩ Kỳ và Hy Lạp
succ[10]4
ĐẦU RA
succ[10]5
Một ví dụ hữu ích hơn
Nó trở nên hữu ích hơn và đồng thời được định hướng toán học hơn trong ví dụ sau. Giả sử ta phải xác định nhiều đa thức bậc 2. Nó có thể trông như thế này
succ[10]6
Điều này có thể được đơn giản hóa bằng cách triển khai hàm "nhà máy" đa thức ngay bây giờ. Chúng ta sẽ bắt đầu với việc viết một phiên bản có thể tạo đa thức bậc 2
$$p[x] = ax^2 + bx + c$$
Việc triển khai Python dưới dạng hàm nhà máy đa thức có thể được viết như thế này
succ[10]7
ĐẦU RA
succ[10]8
Chúng ta có thể khái quát hóa hàm xuất xưởng của mình để nó có thể hoạt động đối với các đa thức có bậc tùy ý
$$\sum_{k=0}^{n} a_{k} \cdot x^{k} = a_{n} \cdot x^{n} + a_{n-1} \cdot x^{n- . + a_{2} \cdot x^{2} + a_{1} \cdot x + a_{0} $$
succ[10]9
ĐẦU RA
110
Ví dụ, hàm p3 thực hiện đa thức sau
$$p_3[x] = x^{5} + 8 \cdot x^{4} - x^{3} + 3 \cdot x + 2 $$
Chức năng đa thức bên trong polynomial_creator trang trí của chúng tôi có thể được triển khai hiệu quả hơn. Chúng ta có thể phân tích thành thừa số theo cách mà nó không cần bất kỳ phép lũy thừa nào
Phiên bản thừa số của đa thức tổng quát mà không có lũy thừa
$$res = [. [a_{n} \cdot x + a_{n-1}] \cdot x +. + a_{1}] \cdot x + a_{0}$$
Triển khai trình tạo trang trí đa thức của chúng tôi để tránh lũy thừa
111
ĐẦU RA
110
Nếu bạn muốn tìm hiểu thêm về đa thức và cách tạo một hạng tử của đa thức, bạn có thể tiếp tục với chương của chúng tôi về Đa thức
Đào tạo Python trực tiếp
Thưởng thức trang này?
Thấy. Tổng quan về các khóa học Python trực tiếp
Các khóa học trực tuyến sắp tới
Khóa học nâng cao chuyên sâu
Python dành cho kỹ sư và nhà khoa học
đăng ký tại đây
Một trang trí đơn giản
Bây giờ chúng tôi đã có mọi thứ sẵn sàng để xác định trình trang trí đơn giản đầu tiên của chúng tôi
113
ĐẦU RA
114
Nếu bạn nhìn vào đầu ra của chương trình trước, bạn có thể thấy điều gì đang xảy ra. Sau phần trang trí "foo = our_decorator[foo]", foo là tham chiếu đến 'function_wrapper'. 'foo' sẽ được gọi bên trong 'function_wrapper', nhưng trước và sau khi gọi, một số mã bổ sung sẽ được thực thi, tôi. e. trong trường hợp của chúng tôi, hai chức năng in
Cú pháp thông thường cho Trình trang trí trong Python
Phần trang trí trong Python thường không được thực hiện theo cách chúng ta đã làm trong ví dụ trước, mặc dù ký hiệu
It's 68.0 degrees!6 rất hấp dẫn và dễ nắm bắt. Đây là lý do, tại sao chúng tôi sử dụng nó. Bạn cũng có thể thấy một vấn đề thiết kế trong cách tiếp cận trước đây của chúng tôi. "foo" tồn tại trong cùng một chương trình ở hai phiên bản, trước khi trang trí và sau khi trang trí
Bây giờ chúng ta sẽ trang trí thích hợp. Trang trí xảy ra trong dòng trước tiêu đề chức năng. "@" được theo sau bởi tên chức năng trang trí
Bây giờ chúng tôi sẽ viết lại ví dụ ban đầu của chúng tôi. Thay vì viết tuyên bố
115
chúng tôi có thể viết
116Nhưng dòng này phải được đặt ngay trước chức năng được trang trí. Ví dụ hoàn chỉnh trông như thế này bây giờ
117
ĐẦU RA
118
Chúng tôi có thể trang trí mọi chức năng khác có một tham số với trình trang trí 'our_decorator' của chúng tôi. Chúng tôi chứng minh điều này trong phần sau. Chúng tôi đã thay đổi một chút trình bao hàm của mình để chúng tôi có thể thấy kết quả của các lệnh gọi hàm
119
ĐẦU RA
del succ successor[10]0
Cũng có thể trang trí các chức năng của bên thứ ba, e. g. các chức năng chúng tôi nhập từ một mô-đun. Chúng ta không thể sử dụng cú pháp Python có dấu "at" trong trường hợp này
del succ successor[10]1
ĐẦU RA
del succ successor[10]2
Đào tạo Python trực tiếp
Thưởng thức trang này?
Thấy. Tổng quan về các khóa học Python trực tiếp
đăng ký tại đây
Mở rộng hàm lượng giác của toán học
Hãy để chúng tôi tạo một trình trang trí hữu ích hơn cho các hàm lượng giác. Nếu bạn xem phần trợ giúp của
It's 68.0 degrees!7,
It's 68.0 degrees!8 hoặc các hàm lượng giác khác của mô-đun toán học, bạn sẽ thấy rằng các đối số của to là giá trị rad. Nếu bạn thích sử dụng độ thì sao?
del succ successor[10]3
ĐẦU RA
del succ successor[10]4
del succ successor[10]5
ĐẦU RA
del succ successor[10]6
Bây giờ, chúng ta có thể áp dụng giá trị cho
It's 68.0 degrees!7 hoặc các hàm lượng giác khác
del succ successor[10]7
ĐẦU RA
del succ successor[10]8Chúng ta cũng có thể mở rộng các hàm lượng giác bằng một công cụ trang trí, tự động biến độ thành giá trị radian. Chúng tôi thêm một tham số khác vào các chức năng
del succ successor[10]9
110
ĐẦU RA
111
Nói chung, chúng ta có thể nói rằng một trình trang trí trong Python là một đối tượng Python có thể gọi được, được sử dụng để sửa đổi một hàm, phương thức hoặc định nghĩa lớp. Đối tượng ban đầu, đối tượng sẽ được sửa đổi, được chuyển đến trình trang trí làm đối số. Trình trang trí trả về một đối tượng đã sửa đổi, e. g. một hàm đã sửa đổi, được liên kết với tên được sử dụng trong định nghĩa
Hàm_wrapper trước chỉ hoạt động đối với các hàm có chính xác một tham số. Chúng tôi cung cấp một phiên bản tổng quát của function_wrapper, phiên bản này chấp nhận các hàm có tham số tùy ý trong ví dụ sau
112
ĐẦU RA
113
Sử dụng nhiều trang trí
Có thể trang trí các chức năng với nhiều hơn một trình trang trí
114
ĐẦU RA
115
Đầu ra cho chúng ta thấy rằng chức năng
1100 được trang trí đầu tiên với
1101, i. e. trình trang trí trực tiếp trên định nghĩa hàm. Sau đó, nó được trang trí bằng
1102 và hơn nữa là
1103
Khi chúng ta gọi chức năng trang trí nhiều lần, nó hoạt động theo cách khác
116
ĐẦU RA
117
Đào tạo Python trực tiếp
Thưởng thức trang này?
Thấy. Tổng quan về các khóa học Python trực tiếp
đăng ký tại đây
Các trường hợp sử dụng cho người trang trí
Kiểm tra các đối số với một Decorator
Trong chương về hàm đệ quy, chúng ta đã giới thiệu hàm giai thừa. Chúng tôi muốn giữ chức năng đơn giản nhất có thể và chúng tôi không muốn che khuất ý tưởng cơ bản, vì vậy chúng tôi đã không kết hợp bất kỳ kiểm tra đối số nào. Vì vậy, nếu ai đó gọi hàm của chúng ta bằng đối số phủ định hoặc bằng đối số float, thì hàm của chúng ta sẽ rơi vào vòng lặp vô tận
Chương trình sau sử dụng hàm trang trí để đảm bảo rằng đối số được truyền cho hàm giai thừa là một số nguyên dương
118
ĐẦU RA
119
Đếm lời gọi hàm với Decorators
Ví dụ sau sử dụng một trình trang trí để đếm số lần một hàm được gọi. Nói chính xác, chúng ta chỉ có thể sử dụng trình trang trí này cho các hàm có chính xác một tham số
def f[]: def g[]: print["Hi, it's me 'g'"] print["Thanks for calling me"] print["This is the function 'f'"] print["I am calling 'g' now:"] g[] f[]0
ĐẦU RA
def f[]: def g[]: print["Hi, it's me 'g'"] print["Thanks for calling me"] print["This is the function 'f'"] print["I am calling 'g' now:"] g[] f[]1
Chúng tôi đã chỉ ra rằng chúng tôi chỉ có thể sử dụng trình trang trí trước đó cho các hàm nhận chính xác một tham số. Chúng tôi sẽ sử dụng ký hiệu *args và **kwargs để viết các trình trang trí có thể xử lý các hàm có số lượng tham số vị trí và từ khóa tùy ý
def f[]: def g[]: print["Hi, it's me 'g'"] print["Thanks for calling me"] print["This is the function 'f'"] print["I am calling 'g' now:"] g[] f[]2
ĐẦU RA
def f[]: def g[]: print["Hi, it's me 'g'"] print["Thanks for calling me"] print["This is the function 'f'"] print["I am calling 'g' now:"] g[] f[]3
Trang trí với các tham số
Chúng tôi xác định hai trang trí trong đoạn mã sau
def f[]: def g[]: print["Hi, it's me 'g'"] print["Thanks for calling me"] print["This is the function 'f'"] print["I am calling 'g' now:"] g[] f[]4
ĐẦU RA
def f[]: def g[]: print["Hi, it's me 'g'"] print["Thanks for calling me"] print["This is the function 'f'"] print["I am calling 'g' now:"] g[] f[]5
Hai trang trí này gần giống nhau, ngoại trừ lời chào. Chúng tôi muốn thêm một tham số để trình trang trí có thể tùy chỉnh lời chào khi chúng tôi thực hiện trang trí. Chúng ta phải bọc một chức năng khác xung quanh chức năng trang trí trước đó của mình để thực hiện điều này. Bây giờ chúng ta có thể dễ dàng nói "Chào buổi sáng" bằng tiếng Hy Lạp
def f[]: def g[]: print["Hi, it's me 'g'"] print["Thanks for calling me"] print["This is the function 'f'"] print["I am calling 'g' now:"] g[] f[]6
ĐẦU RA
def f[]: def g[]: print["Hi, it's me 'g'"] print["Thanks for calling me"] print["This is the function 'f'"] print["I am calling 'g' now:"] g[] f[]7
Nếu chúng ta không muốn hoặc không thể sử dụng cú pháp trang trí "at", chúng ta có thể thực hiện điều đó bằng các lệnh gọi hàm
def f[]: def g[]: print["Hi, it's me 'g'"] print["Thanks for calling me"] print["This is the function 'f'"] print["I am calling 'g' now:"] g[] f[]8
ĐẦU RA
def f[]: def g[]: print["Hi, it's me 'g'"] print["Thanks for calling me"] print["This is the function 'f'"] print["I am calling 'g' now:"] g[] f[]7
Tất nhiên, chúng tôi không cần định nghĩa bổ sung về "lời chào2". Chúng ta có thể áp dụng trực tiếp kết quả của lệnh gọi "greeting["καλημερα"]" trên "foo"
This is the function 'f' I am calling 'g' now: Hi, it's me 'g' Thanks for calling me0
Đào tạo Python trực tiếp
Thưởng thức trang này?
Thấy. Tổng quan về các khóa học Python trực tiếp
đăng ký tại đây
Sử dụng kết thúc tốt đẹp từ funcools
Cách chúng ta định nghĩa các decorator cho đến nay vẫn chưa tính đến việc các thuộc tính
It's 68.0 degrees!
4 [tên chức năng],11
05 [chuỗi tài liệu] và11
06 [Mô-đun trong đó chức năng được xác định]
của các chức năng ban đầu sẽ bị mất sau khi trang trí
This is the function 'f' I am calling 'g' now: Hi, it's me 'g' Thanks for calling me1
Chúng tôi gọi nó trong chương trình sau
This is the function 'f' I am calling 'g' now: Hi, it's me 'g' Thanks for calling me2
ĐẦU RA
This is the function 'f' I am calling 'g' now: Hi, it's me 'g' Thanks for calling me3
Ta được kết quả "không mong muốn" ở trên
Chúng ta có thể lưu các thuộc tính ban đầu của hàm f, nếu chúng ta gán chúng bên trong bộ trang trí. Chúng tôi thay đổi trang trí trước đó của chúng tôi cho phù hợp
This is the function 'f' I am calling 'g' now: Hi, it's me 'g' Thanks for calling me4
This is the function 'f' I am calling 'g' now: Hi, it's me 'g' Thanks for calling me2
ĐẦU RA
This is the function 'f' I am calling 'g' now: Hi, it's me 'g' Thanks for calling me6
May mắn thay, chúng tôi không phải thêm tất cả mã này vào bộ trang trí của mình để có những kết quả này. Thay vào đó, chúng ta có thể nhập "các gói" trang trí từ functools và trang trí chức năng của chúng ta trong trình trang trí với nó
This is the function 'f' I am calling 'g' now: Hi, it's me 'g' Thanks for calling me7
This is the function 'f' I am calling 'g' now: Hi, it's me 'g' Thanks for calling me2
ĐẦU RA
This is the function 'f' I am calling 'g' now: Hi, it's me 'g' Thanks for calling me6
Các lớp thay vì Chức năng
phương pháp cuộc gọi
Cho đến nay chúng tôi đã sử dụng các chức năng như trang trí. Trước khi chúng ta có thể định nghĩa một trình trang trí là một lớp, chúng ta phải giới thiệu phương thức lớp
1107. Chúng tôi đã đề cập rằng một trình trang trí chỉ đơn giản là một đối tượng có thể gọi được, lấy một hàm làm tham số đầu vào. Một hàm là một đối tượng có thể gọi được, nhưng nhiều lập trình viên Python không biết rằng có những đối tượng có thể gọi được khác. Một đối tượng có thể gọi được là một đối tượng có thể được sử dụng và hoạt động như một hàm nhưng có thể không phải là một hàm. Có thể định nghĩa các lớp theo cách mà các thể hiện sẽ là các đối tượng có thể gọi được. Phương thức
1107 được gọi, nếu thể hiện được gọi là "giống như một hàm", tôi. e. sử dụng dấu ngoặc
def temperature[t]: def celsius2fahrenheit[x]: return 9 * x / 5 + 32 result = "It's " + str[celsius2fahrenheit[t]] + " degrees!" return result print[temperature[20]]0
ĐẦU RA
def temperature[t]: def celsius2fahrenheit[x]: return 9 * x / 5 + 32 result = "It's " + str[celsius2fahrenheit[t]] + " degrees!" return result print[temperature[20]]1
Chúng ta có thể viết một lớp cho hàm fibonacci bằng cách sử dụng phương thức
1107
def temperature[t]: def celsius2fahrenheit[x]: return 9 * x / 5 + 32 result = "It's " + str[celsius2fahrenheit[t]] + " degrees!" return result print[temperature[20]]2
ĐẦU RA
def temperature[t]: def celsius2fahrenheit[x]: return 9 * x / 5 + 32 result = "It's " + str[celsius2fahrenheit[t]] + " degrees!" return result print[temperature[20]]3
Bạn có thể tìm thêm thông tin về phương pháp
1107 trong chương Hàm ma thuật của hướng dẫn của chúng tôi