Điều gì không đúng về trình trang trí trong Python?

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):
print('Running our function')
return func
def temperature():
return temp
decorator_1(temperature())
3 trong dòng phía trên chức năng “được trang trí”, chẳng hạn…

temp = 0def decorator_1(func):
print('Running our function')
return func
@decorator_1
def temperature():
return temp
print(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):
print('Running our function')
return func
def temperature():
return temp
decorator_1(temperature())
4, chúng ta chỉ đang chạy
temp = 0def decorator_1(func):
print('Running our function')
return func
def temperature():
return temp
decorator_1(temperature())
5, như hình bên dưới…

temp = 0def decorator_1(func):
print('Running our function')
return func
def temperature():
return temp
decorator_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ản

Mẹo. sử dụng

temp = 0def decorator_1(func):
print('Running our function')
return func
def temperature():
return temp
decorator_1(temperature())
6 tích hợp để tăng cường chức năng setter/getter

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):
print('Running our function')
return func
def temperature():
return temp
decorator_1(temperature())
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
temp = 0def decorator_1(func):
print('Running our function')
return func
def temperature():
return temp
decorator_1(temperature())
8 của chúng tôi phải lớn hơn độ không tuyệt đối…

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):
print('Running our function')
return func
def temperature():
return temp
decorator_1(temperature())
7, làm cho mã của chúng ta dễ đọc và năng động hơn…

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):
print('Running our function')
return func
def temperature():
return temp
decorator_1(temperature())
1 để cho ngắn gọn, nhưng các khái niệm đều giống nhau. Không phải
temp = 0def decorator_1(func):
print('Running our function')
return func
def temperature():
return temp
decorator_1(temperature())
2 dễ đọc hơn rất nhiều so với
temp = 0def decorator_1(func):
print('Running our function')
return func
def temperature():
return temp
decorator_1(temperature())
3 sao?

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):
print('Running our function')
return func
def temperature():
return temp
decorator_1(temperature())
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ể…

c = my_vars(500)
print(c._temp) # 500
c._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à Staticmethod

Mẹo. sử dụng

temp = 0def decorator_1(func):
print('Running our function')
return func
def temperature():
return temp
decorator_1(temperature())
5 và
temp = 0def decorator_1(func):
print('Running our function')
return func
def temperature():
return temp
decorator_1(temperature())
6 tích hợp để tăng cường chức năng của lớp

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):
    print('Running our function')
    return func
    def temperature():
    return temp
    decorator_1(temperature())
    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
  • temp = 0def decorator_1(func):
    print('Running our function')
    return func
    def temperature():
    return temp
    decorator_1(temperature())
    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

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):
print('Running our function')
return func
def temperature():
return temp
decorator_1(temperature())
9 của chúng tôi không yêu cầu đối số
c = my_vars(500)
print(c._temp) # 500
c._temp = -10000
print(c._temp) # -1000
0 thông thường, vì vậy nó không thể tham chiếu lớp ngay cả khi nó muốn

4 — Mẹo nhanh

Mẹo. sử dụng

c = my_vars(500)
print(c._temp) # 500
c._temp = -10000
print(c._temp) # -1000
1 để lưu giữ thông tin chức năng

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)
print(c._temp) # 500
c._temp = -10000
print(c._temp) # -1000
2 và __doc__

Để 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)
print(c._temp) # 500
c._temp = -10000
print(c._temp) # -1000
3, đầu ra của câu lệnh in của chúng tôi như sau

temp = 0def decorator_1(func):
print('Running our function')
return func
def temperature():
return temp
decorator_1(temperature())
6

Để 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)
print(c._temp) # 500
c._temp = -10000
print(c._temp) # -1000
1

5 — Tạo công cụ trang trí tùy chỉnh

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í

python decorators inehertance class programming

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)
print(c._temp) # 500
c._temp = -10000
print(c._temp) # -1000
5 hoặc
c = my_vars(500)
print(c._temp) # 500
c._temp = -10000
print(c._temp) # -1000
6 cho từng chức năng, sau đó gọi từng giá trị trong danh sách tương ứng

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)
print(c._temp) # 500
c._temp = -10000
print(c._temp) # -1000
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

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)
print(c._temp) # 500
c._temp = -10000
print(c._temp) # -1000
8. Nếu không có decorator, chúng ta sẽ phải viết điều khiển luồng
c = my_vars(500)
print(c._temp) # 500
c._temp = -10000
print(c._temp) # -1000
9 đó vào từng chức năng

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

Điều gì là đúng về trang trí trong Python?

Trong Python, trình trang trí là mẫu thiết kế cho phép bạn sửa đổi chức năng của một hàm bằng cách gói nó trong một hàm khác . Hàm bên ngoài được gọi là trình trang trí, lấy hàm ban đầu làm đối số và trả về phiên bản đã sửa đổi của nó.

Trình trang trí Python trong Python là gì?

Trình trang trí là mẫu thiết kế trong Python cho phép người dùng thêm chức năng mới vào đối tượng hiện có mà không sửa đổi cấu trúc của đối tượng . Trình trang trí thường được gọi trước khi định nghĩa chức năng bạn muốn trang trí.

Trình trang trí Python tốt cho việc gì?

Bạn sẽ sử dụng trình trang trí khi bạn cần thay đổi hành vi của một hàm mà không cần sửa đổi chính hàm đó . Một vài ví dụ điển hình là khi bạn muốn thêm ghi nhật ký, kiểm tra hiệu suất, thực hiện lưu vào bộ nhớ đệm, xác minh quyền, v.v. Bạn cũng có thể sử dụng một mã khi cần chạy cùng một mã trên nhiều chức năng.

Các loại trình trang trí trong Python là gì?

Trên thực tế, có hai loại trình trang trí trong Python — trình trang trí lớp và trình trang trí chức năng — nhưng tôi sẽ tập trung vào trình trang trí chức năng ở đây. Trước khi chúng ta đi vào các chi tiết thú vị về cách hoạt động của một trình trang trí cơ bản và cách triển khai các trình trang trí của riêng bạn, trước tiên hãy xem lý do tại sao chúng ta cần chúng.