Python Closures: Cách sử dụng và Tại sao?

Trong hướng dẫn này, bạn sẽ tìm hiểu về bao đóng trong Python, cách xác định bao đóng và lý do bạn nên sử dụng nó.

Biến phi địa phương trong một hàm lồng nhau

Trước khi tìm hiểu hàm đóng là gì, trước tiên chúng ta phải hiểu hàm lồng nhau và biến phi địa phương là gì.

Một hàm được xác định bên trong một hàm khác được gọi là một hàm lồng nhau. Các hàm lồng nhau có thể truy cập các biến của phạm vi bao quanh.

Trong Python, các biến không cục bộ này theo mặc định là chỉ đọc 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 phi địa phương) để sửa đổi chúng.

Sau đây là một ví dụ về một hàm lồng nhau truy cập vào một biến không cục bộ.

 def print_msg(msg): # This is the outer enclosing function def printer(): # This is the nested function print(msg) printer() # We execute the function # Output: Hello print_msg("Hello")

Đầu ra

 xin chào

Chúng ta có thể thấy rằng printer()hàm lồng nhau có thể truy cập vào biến msg không cục bộ của hàm bao quanh.

Xác định chức năng đóng cửa

Trong ví dụ trên, điều gì sẽ xảy ra nếu dòng cuối cùng của hàm print_msg()trả về printer()hàm thay vì gọi nó? Điều này có nghĩa là hàm được định nghĩa như sau:

 def print_msg(msg): # This is the outer enclosing function def printer(): # This is the nested function print(msg) return printer # returns the nested function # Now let's try calling this function. # Output: Hello another = print_msg("Hello") another()

Đầu ra

 xin chào

Đó là điều bất thường.

Các print_msg()hàm được gọi với chuỗi "Hello"và chức năng quay trở lại được liên kết với tên khác. Khi gọi another(), thông báo vẫn được ghi nhớ mặc dù chúng tôi đã thực hiện xong print_msg()chức năng.

Kỹ thuật này mà một số dữ liệu ( "Hellotrong trường hợp này) được gắn vào mã được gọi là bao đóng trong Python .

Giá trị này trong phạm vi bao quanh được ghi nhớ ngay cả khi biến đi ra ngoài phạm vi hoặc bản thân hàm bị xóa khỏi không gian tên hiện tại.

Hãy thử chạy phần sau trong trình bao Python để xem kết quả.

 >>> del print_msg >>> another() Hello >>> print_msg("Hello") Traceback (most recent call last):… NameError: name 'print_msg' is not defined

Ở đây, hàm được trả về vẫn hoạt động ngay cả khi hàm gốc đã bị xóa.

Khi nào chúng ta đóng cửa?

Như đã thấy từ ví dụ trên, chúng ta có một bao đóng trong Python khi một hàm lồng nhau tham chiếu đến một giá trị trong phạm vi bao quanh của nó.

Các tiêu chí phải được đáp ứng để tạo bao đóng trong Python được tóm tắt trong các điểm sau.

  • Chúng ta phải có một hàm lồng nhau (hàm bên trong một hàm).
  • Hàm lồng nhau phải tham chiếu đến một giá trị được xác định trong hàm bao quanh.
  • Hàm bao quanh phải trả về hàm lồng nhau.

Khi nào sử dụng đóng cửa?

Vì vậy, những gì là đóng cửa tốt cho?

Việc đóng cửa có thể tránh việc sử dụng các giá trị toàn cục và cung cấp một số hình thức ẩn dữ liệu. Nó cũng có thể cung cấp một giải pháp hướng đối tượng cho vấn đề.

Khi có một vài phương thức (một phương thức trong hầu hết các trường hợp) được thực hiện trong một lớp, các bao đóng có thể cung cấp một giải pháp thay thế và thanh lịch hơn. Nhưng khi số lượng thuộc tính và phương thức lớn hơn, tốt hơn nên triển khai một lớp.

Đây là một ví dụ đơn giản trong đó việc đóng có thể thích hợp hơn là xác định một lớp và tạo các đối tượng. Nhưng sở thích là của bạn.

 def make_multiplier_of(n): def multiplier(x): return x * n return multiplier # Multiplier of 3 times3 = make_multiplier_of(3) # Multiplier of 5 times5 = make_multiplier_of(5) # Output: 27 print(times3(9)) # Output: 15 print(times5(3)) # Output: 30 print(times5(times3(2)))

Đầu ra

 27 15 30

Trình trang trí Python cũng sử dụng rộng rãi các bao đóng.

Trên một lưu ý kết luận, thật tốt khi chỉ ra rằng các giá trị được bao gồm trong hàm đóng có thể được tìm ra.

Tất cả các đối tượng hàm đều có một __closure__thuộc tính trả về một loạt các đối tượng ô nếu nó là một hàm đóng. Tham khảo ví dụ trên, chúng ta đã biết times3times5là các hàm đóng.

 >>> make_multiplier_of.__closure__ >>> times3.__closure__ (,)

Đối tượng ô có thuộc tính cell_contents lưu trữ giá trị đã đóng.

 >>> times3.__closure__(0).cell_contents 3 >>> times5.__closure__(0).cell_contents 5

thú vị bài viết...