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ăngCá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 PythonVâ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