Trình trang trí Python để kiểm tra đối số

Hãy tưởng tượng bạn là nhà phát triển chính của một số dự án. Một ngày nọ, sếp của bạn nói với bạn rằng bạn và nhóm của bạn phải đảm bảo rằng tất cả các chức năng và phương pháp đều được triển khai các giao thức ghi nhật ký và kiểm tra loại và nó cần được kiểm tra vào cuối tuần

Bạn biết rằng dự án bao gồm 100 đối tượng có thể gọi được. hàm, lớp, trình tạo, v.v. , và bạn đang ở đây, được sếp của bạn hỏi khi nào bạn có thể hoàn thành và thực hiện nhiệm vụ to lớn này

Bạn nhấp một ngụm cà phê, nhìn sếp và nói. "Trong một ít giờ nữa"…

Tại sao bạn lại tự tin như vậy?

Vì bạn biết về người trang trí. Nếu nhiệm vụ này được giao cho một lập trình viên/nhà khoa học dữ liệu mới hơn không biết về tính năng khá nâng cao này trong Python, thì có thể mất hàng tuần nếu không muốn nói là hàng tháng để đạt được mức độ linh hoạt cần thiết

Nhưng bạn biết Python nâng cao và bạn hoàn thành công việc

Trong vài phần tiếp theo, chúng ta sẽ cùng nhau mô phỏng tình huống này và tôi sẽ chỉ cho bạn biết decorator thực sự là gì và chúng ta có thể sử dụng chúng như thế nào cho nhiệm vụ đó

Tôi thậm chí sẽ xây dựng một mô-đun nhỏ mà bạn có thể sao chép và chỉnh sửa theo nhu cầu của riêng mình trong dự án của mình

Chức năng

Các hàm của Python cực kỳ mạnh mẽ bởi vì chúng là công dân hạng nhất, chúng có thể được lưu trữ trong các biến và được gọi sau, chúng có thể được chuyển cho các hàm khác và chúng có thể tạo một môi trường (được gọi là phạm vi cục bộ) mà người dùng có thể sử dụng để . Nghĩa là, bạn có thể định nghĩa các hàm bên trong các hàm khác

Trong Python, chúng ta cũng có các hàm ẩn danh gọi là lambda mà chúng ta có thể sử dụng làm công cụ chức năng trong các biểu thức như bản đồ, bộ lọc, v.v.

Hôm nay, chúng ta sẽ xem xét cách sử dụng một số khái niệm này để thay đổi mã động và cải thiện mã đã viết của bạn mà không cần phải viết lại mọi chức năng trong dự án của bạn

Để hiểu decorator, chúng ta cần hiểu các hàm trong Python

Lưu ý rằng đây không phải là các hàm toán học vì trong một số trường hợp đối với một hàm nhất định, cùng một đầu vào không phải lúc nào cũng đảm bảo cùng một đầu ra, nghĩa là một hàm có thể có tác dụng phụ i. e. chức năng có thể phụ thuộc vào phạm vi bên ngoài

Hãy để chúng tôi tạo một chức năng cơ bản trong Python với mục đích sửa đổi nó sau

Hàm mul_times trả về num tự nối của chuỗi msg

Vì vậy, ví dụ mul_times(“HA”, 10) sẽ trả về “HAHAHAHAHAHAHAHAHA”

Đây thực sự là một ví dụ rất hay về một trong những vấn đề với kiểu gõ động. Chức năng này sẽ chạy và tạo ra đầu ra mong muốn nếu nó được cung cấp đúng tham số. Tuy nhiên, nếu vô tình, một người nhập một số nguyên hoặc float làm đối số msg, thì tất cả sẽ vỡ tung

Rõ ràng, trong sản xuất, một lỗ hổng như vậy là không thể chấp nhận được

Sếp của chúng tôi vừa bảo chúng tôi loại bỏ những quả bom tiềm ẩn như vậy bằng cách ghi nhật ký và kiểm tra mọi chức năng trong cơ sở mã

người trang trí

Ý tưởng của một trình trang trí là tạo ra một phạm vi để thao tác một hàm hoặc lớp theo cách được kiểm soát. Chúng tôi làm điều này trong thực tế bằng cách xác định một chức năng bên trong một chức năng khác

Hãy xem xét đoạn mã sau

Khi chúng tôi chạy tệp này, chúng tôi nhận được đầu ra

**************************************************

Vì vậy, những gì chính xác đang xảy ra ở đây?

Ở dòng 22, chúng ta định nghĩa một hàm mới có tên là wrap bằng cách truyền đối tượng hàm mul_times vào nhật ký hàm

Được rồi, nhưng đăng nhập để làm gì?

rất vui vì bạn đã hỏi. ghi nhật ký tạo ra một phạm vi theo chức năng lồng nhau bên trong nó được gọi là trình bao bọc. Bên trong trình bao bọc, chúng tôi thực thi hàm được truyền dưới dạng đối số và lưu trữ kết quả. Trong trường hợp này, mul_times

Chúng tôi tìm kiếm các lỗi loại và trong trường hợp không chắc là chương trình đưa ra lỗi như vậy cho chúng tôi, chúng tôi ghi lại tên hàm và các đối số của nó trong một tệp

Chúng tôi có thể chọn bỏ qua lỗi nhưng vì chúng tôi là những người cuồng kiểm tra kiểu, chúng tôi đã cố tình tăng TypeError và làm hỏng chương trình của mình

Nếu trình bao bọc không gặp sự cố, thì nó sẽ trả về kết quả của hàm mà chúng ta đã truyền vào

Ở đây có một chút khó khăn. ghi nhật ký hàm bên ngoài trả về đối tượng hàm trình bao bọc chứ không phải trình bao bọc giá trị trả về

Bây giờ, khi chúng ta chỉ định ghi nhật ký (mul_times) cho đối tượng được bao bọc và chúng ta gọi gói được bao bọc trên dòng 24, điều đang thực sự xảy ra là như sau

  • Vì trình bao bọc thực sự bằng trình bao bọc bên trong ghi nhật ký theo chỉ định, nên chúng tôi lưu trữ '*' và 50 trong bộ args và chuyển nó dưới dạng tham số cho trình bao hàm hàm
  • Sau đó, chúng tôi chuyển các tham số này tới mul_times và giá trị trả về được lưu trong biến kết quả
  • Vì không có lỗi loại, nên giá trị trả về của wrap là chuỗi ở trên mà chúng ta gọi là kết quả mà sau đó chúng ta sẽ in ra bàn điều khiển

Đoạn mã trên có thể được viết thanh lịch hơn một chút bằng cách sau

Biểu tượng '@' là đường cú pháp cho mã tương đương ở trên và chúng tôi gọi một chức năng như ghi nhật ký, trình trang trí

Tuy nhiên, có một vấn đề, chúng ta không thể chắc chắn rằng các hàm trong Python sẽ đưa ra lỗi loại mỗi khi nhập sai loại. Trên thực tế, hệ thống gõ động của Python đảm bảo rằng chúng ta chỉ gặp lỗi nếu chúng ta cố làm điều gì đó bất hợp pháp với một đối tượng, chẳng hạn như gọi một phương thức mà nó không có.

Đây là một tính năng rất linh hoạt của Python và đối với một số dự án và tập lệnh, nó rất hay, nhưng nếu bạn muốn xây dựng thứ gì đó chắc chắn, được kiểm tra kỹ lưỡng và có thể bảo trì được, bạn cần thực hiện một số thao tác kiểm tra kiểu

May mắn cho chúng tôi, trong các phiên bản Python sau này, nó đi kèm với một hệ thống suy luận kiểu. Hãy xem cách viết đoạn mã trên với phần triển khai này

Trước sự thất vọng của chúng tôi, khi chúng tôi chạy mã này, chúng tôi nhận được… 100

Python từ chối từ bỏ tính linh hoạt năng động đó. Tất nhiên, chúng tôi có thể cố gắng phá vỡ nó bằng tay

Điều này thực sự sẽ hiệu quả, nhưng chúng tôi không muốn viết điều đó trong tất cả các chức năng của mình có tính đến từng loại tham số của từng chức năng. Nó chỉ đơn giản là quá lặp đi lặp lại và ngu ngốc

Thay vào đó, chúng tôi muốn tự động nắm bắt hệ thống suy luận kiểu của Python bằng cách triển khai hệ thống đó trong một trình trang trí

Hãy tạo một trình trang trí type_checker

Hãy kiểm tra nó trên hàm mul_times của chúng ta

Điều này sẽ gặp sự cố với TypeError

nó hoạt động. Bây giờ chúng tôi chỉ cần kết hợp nó với trình trang trí ghi nhật ký, tuy nhiên có một số cải tiến mà chúng tôi có thể thực hiện vào thời điểm này. Việc ghi nhật ký chức năng thực sự không hợp lý nếu chỉ có một phiên bản của chức năng ghi nhật ký và sau đó xảy ra sự cố

Bây giờ chúng ta đã xử lý việc kiểm tra kiểu, chúng ta có thể chỉ cần thay đổi trình trang trí ghi nhật ký để ghi nhật ký tất cả các chức năng và sau đó để trình trang trí type_checker thực hiện phần còn lại

Chúng tôi sẽ làm điều này bằng cách thay đổi trình trang trí ghi nhật ký và sau đó lồng trình trang trí bằng cách viết chúng lên nhau

Mặc dù vậy, chúng tôi vẫn muốn ghi nhật ký các chức năng bị lỗi. Hãy xem xét đoạn mã sau

Tất nhiên, chương trình đưa ra TypeError và nhật ký. tập tin txt trông như thế này

Function: wrapper succeeded with args: ('3', 50, {})
Function: wrapper succeeded with args: ('Kasper', 10, {})
Function: wrapper succeeded with args: ('0', 0, {})
Function: wrapper succeeded with args: ('Medium', 1, {})
Function: wrapper failed with args: (-1, 100, {})

Hmmm không chính xác những gì chúng ta đã hy vọng, phải không?

Thật tuyệt khi chúng ta có thể đặt các decorator chồng lên nhau để xâu chuỗi chúng, nhưng tại sao chúng không nhận ra tên hàm?

Tôi sẽ để lại câu trả lời dưới dạng bài tập cho người đọc, nhưng tôi sẽ khắc phục sự cố ở đây bằng một số phương pháp hay nhất về Python

Hóa ra thư viện functools đã thấy điều này sắp xảy ra và chúng ta có thể mượn một công cụ trang trí từ functools có tên là kết thúc

Hãy xem nhật ký. txt lại

Function: mul_times succeeded with args: ('3', 50, {})
Function: mul_times succeeded with args: ('Kasper', 10, {})
Function: mul_times succeeded with args: ('0', 0, {})
Function: mul_times succeeded with args: ('Medium', 1, {})
Function: mul_times failed with args: (-1, 100, {})

Khá tốt

Nhưng chúng ta có thể thực hiện một cải tiến khác cho công cụ trang trí ghi nhật ký của mình. Ngay bây giờ Nó chỉ ghi lại một chức năng không thành công nếu lỗi là TypeError và nó không ghi lại thời gian

Chúng tôi muốn nó linh hoạt hơn

Cuối cùng, hãy nêu ra một lỗi dễ hiểu hơn trong trình trang trí type_checker

Điều này sẽ tạo ra ZeroDivisionError và nếu chúng tôi kiểm tra tệp nhật ký, chúng tôi sẽ thấy đầu ra sau

Thật tuyệt, nhưng chúng tôi có hầu hết các phương thức trong mã của chúng tôi không có nhiều chức năng. Làm thế nào để chúng ta trang trí những?

Phương pháp trang trí trong Python

Vâng, điều này gần như giống nhau. Trên thực tế, đối với hầu hết các ứng dụng của trình trang trí, bạn không phải thay đổi bất cứ điều gì. Tuy nhiên, chúng tôi đang thao tác với các đối số có thể gọi được và trong một phương thức, chúng tôi luôn có một tham chiếu đến một thể hiện của lớp làm đối số (trừ khi đó là một phương thức tĩnh)

Điều này hơi khó chịu, nhưng nó sẽ không ngăn cản chúng tôi. Trong trình trang trí type_checker, chúng tôi sẽ không kiểm tra tham số self. Chúng ta cần viết tuyên bố từ chối trách nhiệm mặc dù điều này sẽ chỉ hoạt động nếu chúng ta đặt tên cho đối số cá thể là “self”. Điều này không bắt buộc trong Python mặc dù tôi chưa thấy nó có tên gì khác

Vì vậy, tôi nghĩ rằng đó là một giả định ổn để thực hiện

Tại thời điểm này, chúng tôi lưu ý một lỗ hổng nhỏ trong mã. Trong trình trang trí type_checker, chúng tôi tra cứu trong từ điển. Nếu chúng ta muốn trang trí một hàm chỉ với một phần hoặc không khai báo kiểu, chúng ta sẽ gặp lỗi KeyError

Tôi không muốn có KeyError

Bạn không muốn có KeyError

Không ai muốn một KeyError

Hãy sửa lỗi này để chúng ta có thể trang trí bất kỳ chức năng hoặc phương thức nào bất kể khai báo kiểu nào và sau đó hãy kiểm tra nó

Mã đầy đủ cho đến thời điểm này với bản sửa lỗi trên được triển khai

Đầu ra từ mã này là

Điều đó thật tuyệt. Nhưng nếu bạn có một lớp quái vật với hàng trăm phương thức cần được kiểm tra kiểu và ghi lại thì sao?

Đây là nơi những người trang trí lớp học thực sự tỏa sáng

Chúng ta sẽ tạo một trình trang trí lớp được gọi là togging (tôi đoán là cắt bớt kiểm tra kiểu và ghi nhật ký) sẽ trang trí một lớp và tự động trang trí tất cả các phương thức với type_checker và ghi nhật ký

Cũng sắp đến lúc chia mã ra một chút. Hãy để chúng tôi biến điều này thành một mô-đun mà chúng tôi có thể sử dụng trong một tệp hoặc dự án khác

Mô-đun có tên trang trí của chúng tôi giờ trở thành mô-đun sau

Bây giờ, trong một tệp khác, chúng tôi nhập trang trí lớp togging và kiểm tra nó. Lưu ý rằng khi chúng tôi nhập các bộ trang trí mô-đun, điều đầu tiên xảy ra là tệp là các bộ trang trí. py thực sự được thực thi, vì vậy tệp nhật ký bị xóa. Đây là một điều tốt vì bạn không muốn làm lộn xộn tệp nhật ký của mình với nhiều lần chạy

Đầu ra từ tệp này trong nhật ký. txt

Ngay cả phương pháp __init__ cũng được trang trí

Điều cuối cùng cần cải thiện một lần nữa là trình trang trí ghi nhật ký. Bạn thấy đấy, lưu nhật ký mỗi khi một hàm hoặc phương thức được gọi là tốt trong một số trường hợp, tuy nhiên, nếu bạn đang gọi một hàm hoặc phương thức trong một vòng lặp hoặc một phương thức áp dụng, nó sẽ làm chậm chương trình đáng kể

Chắc chắn sẽ rất tuyệt nếu chúng ta có thể chọn lưu nhật ký hoặc chỉ in nó ra bàn điều khiển. Thao tác I/O của in thực tế cũng làm chậm chương trình nhưng gần như không bằng FS I/O của thao tác mở-ghi-đóng tệp

Chúng tôi cần có khả năng chuyển một đối số cho trình trang trí ghi nhật ký chỉ định xem chúng tôi muốn lưu nhật ký hay chỉ in nó

Chúng tôi làm điều này bằng các chức năng lồng ba. Thoạt nhìn có vẻ hơi phức tạp, nhưng bạn thực sự chỉ nên nghĩ về định nghĩa hàm như tạo ra một khung cảnh hoặc phạm vi cho logic bên trong.

Để hoàn thiện, chúng tôi cũng sẽ tính thời gian cho các chức năng và đưa nó vào nhật ký

Mô-đun sau đây là mô-đun cuối cùng. Hãy sao chép nó, sửa đổi nó để phù hợp với nhu cầu của bạn và sử dụng nó trong công việc hoặc dự án của riêng bạn

Những gì chúng ta có ở đây là một mô-đun nhẹ cung cấp cho chúng ta khả năng kiểm tra kiểu nghiêm ngặt theo nghĩa là nếu bạn trang trí hàm hoặc phương thức của mình có chứa các tham số đã nhập trong định nghĩa bằng trình trang trí trình kiểm tra kiểu và nếu hàm được cung cấp một đối số có kiểu xung đột

Nếu bạn muốn bỏ chọn một số hoặc tất cả các tham số, điều đó không sao cả. Bạn chỉ đơn giản là không chỉ định một loại trong định nghĩa hàm. Người trang trí sẽ tự tìm ra điều này

Nếu muốn, bạn có thể xếp công cụ trang trí ghi nhật ký lên trên nhưng bạn cũng có thể chỉ sử dụng công cụ trang trí đó một mình. Nó tự hoạt động hoàn toàn tốt

Hãy để chúng tôi kiểm tra lại điều này trong một tệp khác

Đầu ra từ chương trình này là kết quả sau được in ra thiết bị đầu cuối/bảng điều khiển do bộ trang trí togging

và trong nhật ký. txt mà chúng ta có

Bây giờ trở lại câu chuyện của chúng tôi

Sếp của chúng tôi yêu cầu chúng tôi đăng nhập và kiểm tra loại (nếu có thể) bất kỳ chức năng hoặc phương thức nào trong dự án lớn của chúng tôi. Bây giờ bạn có một mô-đun thực hiện điều đó. Bạn chỉ cần trang trí bất kỳ lớp nào bằng togging và trang trí bất kỳ định nghĩa hàm nào bằng các trình trang trí ghi nhật ký và type_checker. Chỉ cần lưu ý rằng thứ tự trang trí không quan trọng ở đây

Tôi hy vọng rằng bạn thấy bài viết này hữu ích và bạn có thể thấy những người trang trí có thể mạnh mẽ như thế nào. Chúng tôi thậm chí không nói về bộ nhớ đệm, trình trang trí trạng thái và kết nối với siêu dữ liệu. Tôi sẽ làm điều đó trong các bài viết trong tương lai

Trình trang trí được sử dụng ở mọi nơi trong thư viện tiêu chuẩn và tôi nghĩ đó là một trong những tính năng thú vị và hữu ích nhất của Python

Trình trang trí Python có thể lấy đối số không?

Trình trang trí hàm trong Python chỉ là một hàm nhận một hàm khác làm đối số , mở rộng chức năng của hàm được trang trí mà không thay đổi cấu trúc của nó. Một trình trang trí bao bọc một chức năng khác, khuếch đại hành vi của nó và trả về nó. Khái niệm về trình trang trí trong Python giúp giữ cho mã của bạn KHÔ.

Đối số của lớp trang trí trong Python là gì?

Trình trang trí Python là hàm nhận một hàm làm đối số và trả về một hàm khác làm giá trị trả về . Giả định cho một trình trang trí là chúng ta sẽ truyền một hàm làm đối số và chữ ký của hàm bên trong trong trình trang trí phải khớp với hàm để trang trí.

4 loại đối số trong Python là gì?

Trong Python, chúng ta có 4 loại đối số hàm sau. .
Đối số mặc định
Đối số từ khóa (đối số được đặt tên)
đối số vị trí
Đối số tùy ý (đối số có độ dài thay đổi *args và **kwargs )

3 loại đối số trong Python là gì?

Do đó, chúng tôi kết luận rằng Đối số hàm Python và ba loại đối số hàm của nó. Đây là- các đối số mặc định, từ khóa và tùy ý .