Bạn có muốn viết mã ngắn gọn, dễ đọc và hiệu quả không?
Ảnh của Mauricio Muñoz trên Bapt
Trong chương 7 của Fluent Python, Luciano Ramalho thảo luận về các bộ trang trí và đóng cửa. Chúng không quá phổ biến trong công việc DS cơ bản, tuy nhiên khi bạn bắt đầu xây dựng các mô hình sản xuất viết mã không đồng bộ, chúng trở thành một công cụ vô giá
Không có gì khó chịu, hãy đi sâu vào
1 — Trang trí là gì?Trước khi đi vào các mẹo, hãy tìm hiểu cách thức hoạt động của các công cụ trang trí
Trình trang trí đơn giản là các chức năng lấy chức năng làm đầu vào. Về mặt cú pháp, chúng thường được mô tả là
temp = 0def decorator_1[func]:3 trong dòng phía trên chức năng “được trang trí”, chẳng hạn…
print['Running our function']
return funcdef temperature[]:
return tempdecorator_1[temperature[]]
temp = 0def decorator_1[func]:
print['Running our function']
return func@decorator_1
def temperature[]:
return tempprint[temperature[]]
Tuy nhiên, điều thực sự đang diễn ra là khi chúng ta gọi
temp = 0def decorator_1[func]:4, chúng ta chỉ đang chạy
print['Running our function']
return funcdef temperature[]:
return tempdecorator_1[temperature[]]
temp = 0def decorator_1[func]:5, như hình bên dưới…
print['Running our function']
return funcdef temperature[]:
return tempdecorator_1[temperature[]]
temp = 0def decorator_1[func]:
print['Running our function']
return funcdef temperature[]:
return tempdecorator_1[temperature[]]
Ok, vậy decorators là các hàm lấy một hàm khác làm đối số. Nhưng tại sao chúng ta lại muốn làm điều này?
Chà, những người trang trí thực sự linh hoạt và mạnh mẽ. Chúng thường được sử dụng cho các cuộc gọi lại không đồng bộ và lập trình kiểu chức năng. Chúng cũng có thể được tận dụng để xây dựng chức năng giống như lớp thành các chức năng, do đó giảm thời gian phát triển và mức tiêu thụ bộ nhớ
Hãy cùng tìm hiểu một số mẹo…
2 — Người trang trí tài sảnMẹo. sử dụng
temp = 0def decorator_1[func]:6 tích hợp để tăng cường chức năng setter/getter
print['Running our function']
return funcdef temperature[]:
return tempdecorator_1[temperature[]]
Một trong những công cụ trang trí tích hợp phổ biến nhất là
temp = 0def decorator_1[func]:7. Nhiều ngôn ngữ OOP, chẳng hạn như Java và C++, đề xuất sử dụng mô hình getter/setter. Các hàm này được sử dụng để đảm bảo rằng biến của chúng ta sẽ không trả về/được gán giá trị không chính xác. Một ví dụ có thể yêu cầu biến
print['Running our function']
return funcdef temperature[]:
return tempdecorator_1[temperature[]]
temp = 0def decorator_1[func]:8 của chúng tôi phải lớn hơn độ không tuyệt đối…
print['Running our function']
return funcdef temperature[]:
return tempdecorator_1[temperature[]]
Chúng ta có thể tăng thêm rất nhiều chức năng này bằng cách sử dụng phương pháp
temp = 0def decorator_1[func]:7, làm cho mã của chúng ta dễ đọc và năng động hơn…
print['Running our function']
return funcdef temperature[]:
return tempdecorator_1[temperature[]]
Lưu ý rằng chúng tôi đã xóa tất cả logic điều kiện khỏi
temp = 0def decorator_1[func]:1 để cho ngắn gọn, nhưng các khái niệm đều giống nhau. Không phải
print['Running our function']
return funcdef temperature[]:
return tempdecorator_1[temperature[]]
temp = 0def decorator_1[func]:2 dễ đọc hơn rất nhiều so với
print['Running our function']
return funcdef temperature[]:
return tempdecorator_1[temperature[]]
temp = 0def decorator_1[func]:3 sao?
print['Running our function']
return funcdef temperature[]:
return tempdecorator_1[temperature[]]
Nhưng trước khi chúng ta tiếp tục, có một caviat quan trọng. Trong python, không có thứ gọi là biến riêng tư. Tiền tố
temp = 0def decorator_1[func]:4 chỉ ra rằng biến được bảo vệ và không được tham chiếu bên ngoài lớp. Tuy nhiên, bạn vẫn có thể…
print['Running our function']
return funcdef temperature[]:
return tempdecorator_1[temperature[]]
c = my_vars[500]
print[c._temp] # 500c._temp = -10000
print[c._temp] # -1000
Thực tế là các biến thực sự riêng tư không tồn tại trong python là một lựa chọn thiết kế thú vị. Lập luận là các biến riêng tư trong OOP không thực sự riêng tư — nếu ai đó muốn truy cập chúng, họ có thể thay đổi mã của lớp nguồn và đặt các biến ở chế độ công khai
Python khuyến khích “phát triển có trách nhiệm” và cho phép bạn truy cập từ bên ngoài vào bất cứ thứ gì trong một lớp
3 — Classmethod và StaticmethodMẹo. sử dụng
temp = 0def decorator_1[func]:5 và
print['Running our function']
return funcdef temperature[]:
return tempdecorator_1[temperature[]]
temp = 0def decorator_1[func]:6 tích hợp để tăng cường chức năng của lớp
print['Running our function']
return funcdef temperature[]:
return tempdecorator_1[temperature[]]
Hai trang trí này thường bị nhầm lẫn, nhưng sự khác biệt của chúng rất đơn giản
temp = 0def decorator_1[func]:
7 lấy lớp làm tham số. Nó bị ràng buộc với chính lớp đó, không phải thể hiện của lớp. Do đó, nó có thể truy cập hoặc sửa đổi lớp trên tất cả các phiên bản
print['Running our function']
return funcdef temperature[]:
return tempdecorator_1[temperature[]]temp = 0def decorator_1[func]:
8 không lấy lớp làm tham số. Nó bị ràng buộc với thể hiện của lớp, không phải chính lớp đó. Do đó, nó hoàn toàn không thể truy cập hoặc sửa đổi lớp
print['Running our function']
return funcdef temperature[]:
return tempdecorator_1[temperature[]]
Hãy xem một ví dụ…
Trường hợp sử dụng lớn nhất cho các phương thức lớp là khả năng đóng vai trò là hàm tạo thay thế cho lớp của chúng ta, điều này thực sự hữu ích cho tính đa hình. Ngay cả khi bạn không làm những thứ điên rồ với tính kế thừa, thì vẫn rất tuyệt khi có thể khởi tạo các phiên bản khác nhau của lớp mà không cần câu lệnh if/else
Mặt khác, các phương thức tĩnh thường được sử dụng làm các hàm tiện ích hoàn toàn độc lập với trạng thái của lớp. Lưu ý rằng hàm
temp = 0def decorator_1[func]:9 của chúng tôi không yêu cầu đối số
print['Running our function']
return funcdef temperature[]:
return tempdecorator_1[temperature[]]
c = my_vars[500]0 thông thường, vì vậy nó không thể tham chiếu lớp ngay cả khi nó muốn
print[c._temp] # 500c._temp = -10000
print[c._temp] # -1000
Mẹo. sử dụng
c = my_vars[500]1 để lưu giữ thông tin chức năng
print[c._temp] # 500c._temp = -10000
print[c._temp] # -1000
Hãy nhớ rằng, các bộ trang trí chỉ là các hàm lấy một hàm khác làm đối số. Vì vậy, khi chúng ta gọi các hàm được trang trí, thực ra chúng ta đang gọi hàm trang trí trước. Luồng này ghi đè thông tin về chức năng được trang trí, chẳng hạn như các trường
c = my_vars[500]2 và __doc__
print[c._temp] # 500c._temp = -10000
print[c._temp] # -1000
Để khắc phục vấn đề này, chúng ta có thể tận dụng một decorator khác…
Không có trình trang trí
c = my_vars[500]3, đầu ra của câu lệnh in của chúng tôi như sau
print[c._temp] # 500c._temp = -10000
print[c._temp] # -1000
temp = 0def decorator_1[func]:6
print['Running our function']
return funcdef temperature[]:
return tempdecorator_1[temperature[]]
Để tránh ghi đè thông tin chức năng quan trọng, hãy đảm bảo sử dụng trình trang trí
c = my_vars[500]15 — Tạo công cụ trang trí tùy chỉnh
print[c._temp] # 500c._temp = -10000
print[c._temp] # -1000
Mẹo. xây dựng các công cụ trang trí của riêng bạn để tăng cường quy trình làm việc của bạn, nhưng hãy cẩn thận
Phạm vi biến trong trang trí hơi lạ. Chúng tôi không có thời gian đi sâu vào chi tiết, nhưng đây là bài viết dài 29 phút nếu bạn tận tâm. Chỉ cần lưu ý rằng nếu bạn gặp lỗi sau, hãy đọc phạm vi trang trí
Với tuyên bố từ chối trách nhiệm đó, hãy tiếp tục và xem xét một số công cụ trang trí tùy chỉnh hữu ích…
5. 1 — Chức năng lưu trữ dựa trên Decorator
Đoạn mã dưới đây nối các hàm vào danh sách khi chúng được gọi
Một trường hợp sử dụng tiềm năng là để thử nghiệm đơn vị, giống như với pytest. Giả sử rằng chúng ta có các bài kiểm tra nhanh và chậm. Thay vì gán thủ công từng cái vào một danh sách riêng biệt, chúng ta chỉ cần thêm một trình trang trí
c = my_vars[500]5 hoặc
print[c._temp] # 500c._temp = -10000
print[c._temp] # -1000
c = my_vars[500]6 cho từng chức năng, sau đó gọi từng giá trị trong danh sách tương ứng
print[c._temp] # 500c._temp = -10000
print[c._temp] # -1000
5. 2 — Truy vấn dữ liệu thời gian hoặc đào tạo mô hình
Đoạn mã dưới đây in thời gian chạy chức năng của bạn
Nếu bạn đang chạy bất kỳ loại truy vấn dữ liệu nào hoặc đào tạo một mô hình có nhật ký xấu, thì việc ước tính thời gian chạy sẽ thực sự hữu ích. Chỉ cần có một trình trang trí
c = my_vars[500]7, bạn có thể nhận được các ước tính thời gian chạy cho bất kỳ chức năng nào
print[c._temp] # 500c._temp = -10000
print[c._temp] # -1000
5. 3 — Thực hiện điều khiển luồng trên các đầu vào chức năng
Đoạn mã dưới đây thực hiện kiểm tra có điều kiện đối với các tham số chức năng trước khi thực hiện chức năng
Trình trang trí này áp dụng logic có điều kiện trên tất cả tham số của các chức năng của chúng tôi là
c = my_vars[500]8. Nếu không có decorator, chúng ta sẽ phải viết điều khiển luồng
print[c._temp] # 500c._temp = -10000
print[c._temp] # -1000
c = my_vars[500]9 đó vào từng chức năng
print[c._temp] # 500c._temp = -10000
print[c._temp] # -1000
Và đây chỉ là một vài ví dụ. Trang trí có thể thực sự hữu ích
Cảm ơn vì đã đọc. Tôi sẽ viết thêm 18 bài viết mang lại nghiên cứu học thuật cho ngành DS. Kiểm tra nhận xét của tôi để biết các liên kết đến nguồn chính cho bài đăng này và một số tài nguyên hữu ích