Python trang trí lồng nhau với các đối số
Khi tôi lần đầu tiên biết về các công cụ trang trí Python, việc sử dụng chúng giống như làm phép thuật voodoo. Trình trang trí có thể cung cấp cho bạn khả năng thêm các chức năng mới vào bất kỳ chức năng nào có thể gọi được mà không thực sự chạm vào hoặc thay đổi mã bên trong nó. Điều này thường có thể mang lại khả năng đóng gói tốt hơn và giúp bạn viết mã rõ ràng và dễ hiểu hơn. Tuy nhiên, decorator được coi là một chủ đề khá nâng cao trong Python vì để hiểu và viết nó yêu cầu bạn phải có lệnh đối với nhiều khái niệm bổ sung như đối tượng hạng nhất, hàm bậc cao hơn, bao đóng, v.v. Đầu tiên, tôi sẽ cố gắng giới thiệu những khái niệm này khi cần thiết và sau đó làm sáng tỏ khái niệm cốt lõi của lớp trang trí theo từng lớp. Vì vậy, hãy đi sâu vào Show
Đối tượng hạng nhấtTrong Python, về cơ bản mọi thứ đều là đối tượng và các hàm được coi là đối tượng hạng nhất. Điều đó có nghĩa là các hàm có thể được truyền xung quanh và được sử dụng làm đối số, giống như bất kỳ đối tượng nào khác (chuỗi, int, float, danh sách, v.v.). Bạn có thể gán các hàm cho các biến và xử lý chúng như bất kỳ đối tượng nào khác. Hãy xem xét ví dụ này
Ví dụ trên minh họa cách Python coi các chức năng là công dân hạng nhất. Đầu tiên, tôi định nghĩa hai hàm, 6 và 7, sau đó 8 lấy chúng làm tham số. 8 chạy các hàm được lấy làm tham số và in kết quả. Sau đó, chúng tôi gán 8 cho biến 1. Cuối cùng, chúng tôi chạy 1 và nó hoạt động giống như 8Hàm bậc cao hơnPython cũng cho phép bạn sử dụng các hàm làm giá trị trả về. Bạn có thể nhận một hàm khác và trả về hàm đó hoặc bạn có thể định nghĩa một hàm bên trong một hàm khác và trả về hàm bên trong 0 1Bây giờ bạn có thể gán kết quả của 4 cho một biến khác và thực hiện chức năng đầu ra 3 4Hãy xem xét một ví dụ khác nơi bạn có thể định nghĩa một hàm lồng trong một hàm và trả về hàm lồng thay vì kết quả của nó 5
Lưu ý cách hàm lồng nhau 5 được định nghĩa bên trong hàm 6 và sau đó câu lệnh return của hàm 6 trả về hàm lồng nhau. Sau khi định nghĩa, để đến hàm lồng nhau, đầu tiên chúng ta gọi hàm 6 và nhận kết quả là một hàm khác. Sau đó thực hiện kết quả của hàm 6 in ra thông báo từ hàm 5đóng cửaBạn đã thấy các ví dụ về chức năng bên trong hoạt động trong phần trước. Các hàm lồng nhau có thể truy cập các biến của phạm vi kèm theo. Trong Python, các biến không cục bộ này chỉ được đọc theo mặc định và chúng ta phải khai báo chúng một cách rõ ràng là không cục bộ (sử dụng từ khóa 31) để sửa đổi chúng. Sau đây là một ví dụ về hàm lồng nhau truy cập một biến không cục bộ
Bây giờ chạy chức năng, 3 0Chà, thật bất thường Hàm 32 được gọi với chuỗi 33 và hàm trả về được gắn với tên 34. Khi gọi 35, thông báo vẫn được ghi nhớ và được sử dụng để lấy kết quả mặc dù chức năng bên ngoài 32 đã hoàn thành việc thực hiệnKỹ thuật này mà một số dữ liệu (“deli”) được gắn vào mã được gọi là đóng trong Python. Giá trị trong phạm vi kèm theo được ghi nhớ ngay cả khi biến nằm ngoài phạm vi hoặc chính hàm đó bị xóa khỏi không gian tên hiện tại. Người trang trí sử dụng ý tưởng về các biến không cục bộ nhiều lần và bạn sẽ sớm thấy cách Viết một Decorator cơ bảnVới những điều kiện tiên quyết này, hãy tiếp tục và tạo công cụ trang trí đơn giản đầu tiên của bạn 1Trước khi sử dụng decorator, hãy xác định một hàm đơn giản không có bất kỳ tham số nào 2Coi các chức năng là đối tượng hạng nhất, bạn có thể sử dụng trình trang trí của mình như thế này 3 4Trong hai dòng trên, bạn có thể thấy một trình trang trí rất đơn giản đang hoạt động. Hàm 37 của chúng tôi nhận một hàm mục tiêu, thao tác hàm mục tiêu bên trong hàm 38 và sau đó trả về hàm 38. Chạy hàm được trả về bởi trình trang trí, bạn sẽ nhận được kết quả đã sửa đổi của mình. Nói một cách đơn giản, các trình trang trí bao bọc một chức năng và sửa đổi hành vi của nó
Trước khi chuyển sang phần tiếp theo, hãy xem cách chúng ta có thể lấy giá trị trả về của hàm mục tiêu thay vì chỉ in nó 5Trong ví dụ trên, hàm trình bao bọc trả về kết quả của hàm đích và chính trình bao bọc. Điều này giúp có thể nhận được kết quả của hàm đã sửa đổi 6 7Bạn có đoán được tại sao giá trị trả về của hàm được trang trí lại xuất hiện ở dòng cuối cùng thay vì ở giữa như trước đây không? Đường cú pháp @Cách bạn đã sử dụng công cụ trang trí trong phần trước có thể cảm thấy hơi rắc rối. Trước tiên, bạn phải gõ tên 00 ba lần để gọi và sử dụng trình trang trí. Ngoài ra, việc phân biệt nơi trang trí đang thực sự hoạt động trở nên khó khăn hơn. Vì vậy, Python cho phép bạn sử dụng trình trang trí với cú pháp đặc biệt 01. Bạn có thể áp dụng các công cụ trang trí của mình trong khi xác định các chức năng của mình, như thế này 8Đôi khi cú pháp trên được gọi là cú pháp chiếc bánh và nó chỉ là một cú pháp đường cho 02Trang trí các chức năng với các đối sốTrình trang trí ngây thơ mà chúng tôi đã triển khai ở trên sẽ chỉ hoạt động đối với các hàm không có đối số. Nó sẽ thất bại và tăng 03 nếu bạn cố trang trí một hàm có đối số với 37. Bây giờ, hãy tạo một trình trang trí khác có tên là 05 sẽ nhận một hàm trả về một giá trị chuỗi và chuyển đổi giá trị chuỗi đó thành chữ hoa 9Tạo hàm mục tiêu trả về giá trị chuỗi 00 01 02Hàm 06 lấy một tham số là 07 và trả về một thông báo dưới dạng chuỗi. Hãy xem cách trình trang trí 05 đang sửa đổi chuỗi trả về ban đầu, chuyển đổi chuỗi đó thành chữ hoa và thêm ký hiệu 09 bổ sung mà không thay đổi trực tiếp bất kỳ mã nào trong hàm 06Giải quyết khủng hoảng danh tínhTrong Python, bạn có thể xem xét bất kỳ đối tượng nào và các thuộc tính của nó thông qua trình bao tương tác. Một hàm biết danh tính của nó, chuỗi tài liệu, v.v. Chẳng hạn, bạn có thể kiểm tra hàm 11 tích hợp theo các cách sau 03 04 05 06 07 08 09 10Việc xem xét nội quan này hoạt động tương tự đối với các chức năng mà bạn đã tự xác định. Tôi sẽ sử dụng hàm 06 đã xác định trước đó 11 12 13 14Bây giờ những gì đang xảy ra ở đó. Trình trang trí 05 đã làm cho chức năng 06 bối rối về danh tính của chính nó. Thay vì báo cáo tên của chính nó, nó lấy danh tính của hàm bên trong 38. Điều này có thể gây nhầm lẫn trong khi gỡ lỗi. Bạn có thể sửa lỗi này bằng cách sử dụng trình trang trí 16 dựng sẵn từ mô-đun 17. Điều này sẽ đảm bảo rằng bản sắc ban đầu của chức năng được trang trí vẫn được bảo tồn 15 16 17Xem xét kỹ hàm 06 được trang trí bằng trình trang trí đã sửa đổi sẽ cho bạn kết quả mong muốn 11 19 13 31Trang trí trong tự nhiênTrước khi chuyển sang phần tiếp theo, hãy xem một vài ví dụ thực tế về decorator. Để xác định tất cả các trình trang trí, chúng tôi sẽ sử dụng mẫu sau mà chúng tôi đã hoàn thiện cho đến nay 32hẹn giờTrình trang trí hẹn giờ sẽ giúp bạn định thời gian cho các cuộc gọi của mình theo cách không xâm phạm. Nó có thể giúp bạn trong khi gỡ lỗi và định hình các chức năng của bạn 33Theo cách trên, chúng ta có thể xem xét thời gian cần thiết để hàm 19 hoàn thành quá trình thực thi của nó 34 35Bộ ghi ngoại lệCũng giống như trình trang trí 20, chúng ta có thể định nghĩa trình trang trí logger sẽ ghi lại trạng thái của một hàm có thể gọi được. Đối với phần trình diễn này, tôi sẽ xác định một trình ghi ngoại lệ sẽ hiển thị thông tin bổ sung như dấu thời gian, tên đối số khi một ngoại lệ xảy ra bên trong có thể gọi được trang trí 36Hãy gọi ZeroDivisionError để xem hoạt động của bộ ghi nhật ký 37 38Đầu tiên, trình trang trí in một vài thông tin liên quan đến chức năng và sau đó đưa ra lỗi ban đầu Xác thực & Kiểm tra thời gian chạyHệ thống kiểu của Python được gõ mạnh nhưng rất năng động. Đối với tất cả các lợi ích của nó, điều này có nghĩa là một số lỗi có thể cố gắng len lỏi vào, mà các ngôn ngữ được nhập tĩnh hơn (như Java) sẽ bắt gặp tại thời điểm biên dịch. Nhìn xa hơn nữa, bạn có thể muốn thực thi các kiểm tra tùy chỉnh, phức tạp hơn đối với dữ liệu đi vào hoặc ra. Người trang trí có thể cho phép bạn dễ dàng xử lý tất cả những điều này và áp dụng nó cho nhiều chức năng cùng một lúc Hãy tưởng tượng điều này. bạn có một tập hợp các hàm, mỗi hàm trả về một từ điển, từ điển này (trong số các trường khác) bao gồm một trường có tên là “tóm tắt. ” Giá trị của phần tóm tắt này không được dài quá 30 ký tự; . Đây là một công cụ trang trí làm tăng ValueError nếu điều đó xảy ra 39 40Thử lạiHãy tưởng tượng một tình huống trong đó khả năng gọi được xác định của bạn không thành công do một số vấn đề liên quan đến I/O và bạn muốn thử lại lần nữa. Decorator có thể giúp bạn đạt được điều đó theo cách có thể tái sử dụng. Hãy xác định một trình trang trí 21 sẽ chạy lại chức năng được trang trí nhiều lần nếu xảy ra lỗi http 41 42Áp dụng nhiều trang tríBạn có thể áp dụng nhiều trình trang trí cho một chức năng bằng cách xếp chồng chúng lên nhau. Hãy xác định hai trình trang trí đơn giản và sử dụng cả hai trên một chức năng 43 44Các decorator được gọi theo thứ tự từ dưới lên. Đầu tiên, trình trang trí 22 được áp dụng trên kết quả của hàm 23 và sau đó kết quả của 22 được chuyển đến trình trang trí 25. Ngăn xếp trang trí ở trên có thể được viết là 26. Thay đổi thứ tự của các trang trí và xem điều gì sẽ xảy raTrang trí với ArgumentsTrong khi xác định trình trang trí 21 trong phần trước, bạn có thể nhận thấy rằng tôi đã mã hóa cứng số lần tôi muốn chức năng thử lại nếu xảy ra lỗi. Sẽ rất hữu ích nếu bạn có thể đưa số lần thử làm tham số vào trình trang trí và làm cho nó hoạt động tương ứng. Đây không phải là một nhiệm vụ tầm thường và bạn sẽ cần ba cấp hàm lồng nhau để đạt được điều đóTrước khi làm điều đó, hãy tạo một ví dụ nhỏ về cách bạn có thể xác định các trình trang trí bằng các tham số 45 46Trình trang trí 28 nhận một tham số duy nhất có tên là 29. Nó chia đầu ra chuỗi của hàm được trang trí bằng một khoảng trắng và sau đó nối chúng bằng dấu phân cách do người dùng xác định được chỉ định trong đối số 29. Định nghĩa lồng nhau ba lớp trông có vẻ đáng sợ nhưng chúng ta sẽ làm được điều đó ngay lập tức. Lưu ý cách bạn có thể sử dụng trình trang trí với các tham số khác nhau. Trong ví dụ trên, tôi đã định nghĩa ba hàm khác nhau để chứng minh việc sử dụng 28. Điều quan trọng cần lưu ý là trong trường hợp trình trang trí nhận tham số, bạn sẽ luôn cần truyền một thứ gì đó cho nó và ngay cả khi bạn không muốn truyền bất kỳ tham số nào (chạy với mặc định), bạn vẫn cần phải trang trí . Hãy thử thay đổi decorator trên hàm 34 từ 35 thành 28 và xem điều gì sẽ xảy raThông thường, một trình trang trí tạo và trả về một hàm bao bọc bên trong nhưng ở đây trong trình trang trí 37, có một hàm bên trong bên trong một hàm bên trong khác. Điều này gần giống như một giấc mơ trong một giấc mơ từ bộ phim InceptionCó một vài điều tế nhị xảy ra trong hàm 35
Trang trí có và không có đối sốBạn đã thấy trước đó rằng một trình trang trí được thiết kế đặc biệt để lấy tham số không thể được sử dụng nếu không có tham số; . Nhưng nếu bạn muốn thiết kế một cái có thể sử dụng cả có và không có đối số thì sao?. Hãy xác định lại trình trang trí 28 để bạn có thể sử dụng nó với các tham số hoặc giống như một trình trang trí ít tham số thông thường mà chúng ta đã thấy trước đây 47 48Ở đây, đối số 49 đóng vai trò là điểm đánh dấu, cho biết liệu trình trang trí có được gọi với đối số hay khôngNếu 28 đã được gọi mà không có đối số, hàm được trang trí sẽ được chuyển vào dưới dạng 49. Nếu nó đã được gọi với các đối số, thì 49 sẽ là Không có. Dấu * trong danh sách đối số có nghĩa là các đối số còn lại không thể được gọi là đối số vị trí. Lần này bạn có thể sử dụng 28 có hoặc không có đối số và chức năng 06 và 22 ở trên chứng minh rằngMột mẫu chungCá nhân tôi thấy nó cồng kềnh khi bạn cần ba lớp hàm lồng nhau để xác định một trình trang trí tổng quát có thể được sử dụng có hoặc không có đối số. David Beazly trong cuốn sách Python Cookbook của anh ấy cho thấy một cách tuyệt vời để xác định các trình trang trí tổng quát mà không cần viết ba cấp hàm lồng nhau. Nó sử dụng hàm 56 tích hợp để đạt được điều đó. Sau đây là mẫu bạn có thể sử dụng để xác định các trình trang trí tổng quát theo cách thanh lịch hơn 49Hãy xác định lại trình trang trí 21 của chúng tôi bằng cách sử dụng mẫu này 50 51Trong trường hợp này, bạn không cần phải viết các hàm lồng ba cấp độ và 56 sẽ lo việc đó. Các phần có thể được sử dụng để tạo các hàm dẫn xuất mới có một số tham số đầu vào được gán trước. Khoảng 59 làm như sau 52Điều này giúp loại bỏ sự cần thiết phải viết nhiều lớp chức năng xuất xưởng lồng nhau, có được một trình trang trí tổng quát Xác định Decorators với các lớpLần này, tôi sẽ sử dụng một class để soạn một decorator. Các lớp có thể hữu ích để tránh kiến trúc lồng nhau trong khi viết các bộ trang trí. Ngoài ra, có thể hữu ích khi sử dụng một lớp trong khi viết các trình trang trí trạng thái. Bạn có thể làm theo mẫu bên dưới để soạn các trang trí với các lớp 53Hãy sử dụng mẫu trên để viết một trình trang trí có tên là 60 sẽ thêm các thẻ in đậm 61 vào đầu ra chuỗi của một hàm 54 55Phương thức init() lưu trữ một tham chiếu đến hàm num_calls và có thể thực hiện các khởi tạo cần thiết khác. Phương thức call() sẽ được gọi thay vì hàm được trang trí. Về cơ bản, nó thực hiện giống như hàm 62 trong các ví dụ trước đây của chúng tôi. Lưu ý rằng bạn cần sử dụng hàm 63 thay vì 64Trước khi tiếp tục, hãy viết một trình trang trí trạng thái bằng cách sử dụng các lớp. Các nhà trang trí có trạng thái có thể nhớ trạng thái của lần chạy trước đó của họ. Đây là một công cụ trang trí có trạng thái có tên là 65 sẽ theo dõi số lần các chức năng được trang trí được gọi trong từ điển. Các khóa của từ điển sẽ chứa tên của các chức năng và các giá trị tương ứng sẽ chứa số lượng cuộc gọi 56 57Một vài ví dụ khácGiá trị trả về bộ đệmTrình trang trí có thể cung cấp một cách thanh lịch để ghi nhớ các giá trị trả về của hàm. Hãy tưởng tượng bạn có một API đắt tiền và bạn muốn gọi nó càng ít lần càng tốt. Ý tưởng là lưu và lưu vào bộ đệm các giá trị do API trả về cho các đối số cụ thể, để nếu các đối số đó xuất hiện lại, bạn có thể cung cấp kết quả từ bộ đệm thay vì gọi lại API. Điều này có thể cải thiện đáng kể hiệu suất của ứng dụng của bạn. Ở đây tôi đã mô phỏng một lệnh gọi API đắt tiền và cung cấp bộ nhớ đệm với một trình trang trí 58 59Bạn sẽ thấy rằng việc chạy chức năng này mất khoảng 3 giây. Để cache kết quả, chúng ta có thể sử dụng funcools tích hợp sẵn của Python. lru_cache để lưu kết quả đối với một đối số trong từ điển và phục vụ kết quả đó khi nó gặp lại đối số tương tự. Hạn chế duy nhất ở đây là, tất cả các đối số cần phải được băm 0 1Bộ nhớ đệm ít sử dụng gần đây nhất (LRU) sắp xếp các mục theo thứ tự sử dụng, cho phép bạn nhanh chóng xác định mục nào không được sử dụng trong khoảng thời gian dài nhất. Trong trường hợp trên, tham số 66 đề cập đến số lượng phản hồi tối đa được lưu trước khi bắt đầu xóa những phản hồi sớm nhất. Trong khi bạn chạy chức năng trang trí, bạn sẽ thấy lần đầu tiên sẽ mất khoảng 3 giây để trả về kết quả. Nhưng nếu bạn chạy lại chức năng với cùng một tham số, nó sẽ lấy kết quả từ bộ đệm gần như ngay lập tứcchuyển đổi đơn vịTrình trang trí sau chuyển đổi độ dài từ đơn vị SI sang nhiều đơn vị khác mà không làm ô nhiễm chức năng mục tiêu của bạn với logic chuyển đổi 2Hãy sử dụng nó trên một hàm trả về diện tích hình chữ nhật 3 4Sử dụng công cụ trang trí chuyển đổi trên hàm vùng cho biết cách nó in ra đơn vị chuyển đổi trước khi trả về kết quả mong muốn. Thử nghiệm với các đơn vị chuyển đổi khác và xem điều gì sẽ xảy ra Đăng ký chức năngSau đây là một ví dụ về đăng ký chức năng logger trong khung Flask. Trình trang trí 67 không thực hiện bất kỳ thay đổi nào đối với chức năng 68 được trang trí. Thay vào đó, nó nhận chức năng và đăng ký nó trong một danh sách có tên là 69 mỗi khi nó được gọi 5Nếu bạn chạy máy chủ và nhấn vào url 70, nó sẽ chào đón bạn bằng một thông báo 71. Ngoài ra, bạn sẽ có thể thấy bản in 72 và 73 của yêu cầu http của mình trên thiết bị đầu cuối. Hơn nữa, nếu bạn kiểm tra 69, bạn sẽ tìm thấy thiết bị ghi nhật ký đã đăng ký ở đó. Bạn sẽ tìm thấy nhiều cách sử dụng decorator thực tế hơn trong Flask frameworkNhận xétTất cả các đoạn mã trong blog đã được viết và thử nghiệm với python 3. 8 trên máy chạy Ubuntu 20. 04 Trình trang trí trong Python có thể chấp nhận đối số không?Các đối số của trình trang trí có thể truy cập được đối với trình trang trí bên trong thông qua một bao đóng , chính xác như cách hàm bên trong wrap() có thể truy cập f. Và vì các bao đóng mở rộng đến tất cả các cấp độ của các hàm bên trong, nên arg cũng có thể truy cập được từ bên trong wrap() nếu cần.
Một chức năng có thể có 2 trang trí?Vì vậy, ở đây trong bài đăng này, chúng ta sẽ tìm hiểu về Decorator Chaining. Chuỗi trang trí có nghĩa là áp dụng nhiều hơn một trang trí bên trong một chức năng. Python cho phép chúng ta triển khai nhiều trình trang trí cho một hàm .
Có bao nhiêu trình trang trí có thể được áp dụng cho một hàm trong Python?Chà, có 1000 khả năng . Việc sử dụng cổ điển đang mở rộng một hành vi chức năng từ một lib bên ngoài (bạn không thể sửa đổi nó) hoặc để gỡ lỗi (bạn không muốn sửa đổi nó vì nó là tạm thời). Tất nhiên, điểm hay của decorator là bạn có thể sử dụng chúng ngay lập tức trên hầu hết mọi thứ mà không cần viết lại.
Câu nào sau đây là đúng Bạn không thể xâu chuỗi nhiều bộ trang trí trong Python?Giải thích. Mọi chức năng, bất kể có tham số hay không, đều có thể được trang trí . Do đó tuyên bố là sai. |