39. Đóng cửa và tế bào

Các mã opcode MAKE_CELL, LOAD_DEREF, STORE_DEREF và PyCellObject giúp nắm bắt các biến trong phạm vi kèm theo.

39. Đóng cửa và ô

Các bao đóng cho phép hàm lồng nhau sử dụng các biến từ hàm kèm theo sau khi hàm kèm theo đó trả về.```python def make_adder(n): def add(x): return x + n return add

add10 = make_adder(10) print(add10(5)) Đầu ra:text 15 ```Biếnnthuộc vềmake_adder, Nhưngaddsau này vẫn sử dụng nó. CPython hỗ trợ điều này bằng cách di chuyển các biến đã ghi vào đối tượng ô. Hàm bên trong giữ các tham chiếu đến các ô đó.

39.1 Hàm lồng nhau

Hàm lồng nhau là hàm được xác định bên trong một hàm khác.```python def outer(): def inner(): return 1

return inner

```cácdef innercâu lệnh thực thi khiouterchạy. Nó tạo ra một đối tượng hàm và liên kết nó với tên cục bộinner.

Đang gọiouter()trả về đối tượng hàm đó:python fn = outer() print(fn()) Đối tượng hàm choinnerchứa:text code object globals dictionary defaults keyword defaults annotations closure cells, if needed Nếu nhưinnerkhông tham chiếu đến các biến cục bộ bên ngoài, nó không cần đóng.

39.2 Biến miễn phí

Biến tự do là biến được sử dụng bởi đối tượng mã nhưng được xác định trong phạm vi kèm theo.```python def outer(): x = 10

def inner():
    return x

return inner

```Bên tronginner, xlà một biến tự do.

Bạn có thể kiểm tra điều này:```python def outer(): x = 10

def inner():
    return x

return inner

fn = outer()

print(fn.code.co_freevars) Đầu ra:text ('x',) ```Đối tượng mã bên trong ghi lại những gì nó cầnxtừ bên ngoài.

39.3 Biến ô

Biến ô là biến cục bộ phải được hàm bên trong nắm bắt.```python def outer(): x = 10

def inner():
    return x

return inner

```Vìouter, xlà một biến ô vì hàm bên trong sử dụng nó.

Thanh tra:python print(outer.__code__.co_cellvars) Đầu ra:text ('x',) Biến tương tự được nhìn từ hai hướng:```text outer: x is a cell variable

inner: x is a free variable


Một biến cục bộ bình thường tồn tại trong một khe khung.```python
def f():
    x = 10
    return x
```Sau đó`f`quay trở lại, khung của nó có thể bị phá hủy. Các khe cục bộ của nó biến mất.

Nhưng điều này không thể hoạt động đối với việc đóng cửa:```python
def outer():
    x = 10

    def inner():
        return x

    return inner
```Sau đó`outer`trở lại,`inner`vẫn cần`x`.

CPython giải quyết điều này bằng cách lưu trữ`x`trong một đối tượng ô.

Về mặt khái niệm:```text
outer frame
    x slot points to cell
        cell contains 10

inner function
    closure tuple points to same cell
```Khi`outer`trở lại, khung có thể biến mất, nhưng ô vẫn tồn tại vì`inner`tham khảo nó.

## 39.5 Đối tượng di động

Đối tượng ô là một thùng chứa nhỏ chứa tham chiếu đến một đối tượng Python khác.

Về mặt khái niệm:```text
cell
    contents -> object
```Vì:```python
def outer():
    x = 10
    def inner():
        return x
    return inner
```việc đóng cửa trông giống như:```text
inner function
    __closure__:
        cell for x
            contents: 10
```Kiểm tra nó:```python
fn = outer()

print(fn.__closure__)
print(fn.__closure__[0].cell_contents)
```Hình dạng đầu ra:```text
(<cell at 0x...: int object at 0x...>,)
10
```Tế bào, chứ không phải toàn bộ khung bên ngoài, mới là thứ tồn tại.

## 39.6 Bộ đóng hàm

Một đối tượng hàm có`__closure__`thuộc tính.```python
def outer():
    x = 10

    def inner():
        return x

    return inner

fn = outer()

print(fn.__closure__)

__closure__là một trong haiNonehoặc một bộ các đối tượng ô.

Đối với ví dụ này:text fn.__closure__ -> (cell_for_x,) Thứ tự các ô tương ứng vớifn.__code__.co_freevars.

print(fn.__code__.co_freevars)
for cell in fn.__closure__:
    print(cell.cell_contents)
```Đầu ra:```text
('x',)
10
```## 39.7 Đọc siêu dữ liệu đóng

Sử dụng ví dụ này:```python
def outer(a):
    b = 2

    def inner(c):
        return a + b + c

    return inner

fn = outer(10)

print(outer.__code__.co_cellvars)
print(fn.__code__.co_freevars)
print(fn.__closure__)
```Hình dạng đầu ra:```text
('a', 'b')
('a', 'b')
(<cell ...>, <cell ...>)
```Hàm ngoài có các biến ô`a`Và`b`.

Hàm bên trong có các biến miễn phí`a`Và`b`.

Hàm trả về mang các ô cho cả hai.

## 39,8`LOAD_DEREF`Các biến đóng được truy cập bằng mã byte dereference.

Vì:```python
def outer():
    x = 10

    def inner():
        return x

    return inner

innerkhông sử dụngLOAD_FASTvìx. Nó sử dụng quyền truy cập đóng cửa.

Về mặt khái niệm:```text LOAD_DEREF x RETURN_VALUE


`LOAD_DEREF`đọc nội dung của một ô.

Tương tự, việc lưu trữ vào một biến đóng sử dụng lệnh lưu trữ theo định hướng quy định.

## 39.9 Tháo rời một đóng cửa

sử dụng`dis`:

```python
import dis

def outer():
    x = 10

    def inner():
        return x

    return inner

dis.dis(outer)

fn = outer()
dis.dis(fn)
```Hãy tìm:```text
MAKE_CELL
LOAD_CLOSURE
MAKE_FUNCTION
LOAD_DEREF
STORE_DEREF
```Hướng dẫn chính xác khác nhau tùy theo phiên bản CPython, nhưng các hoạt động khái niệm ổn định:```text
create cell for captured local
create inner function with closure
load from closure cell inside inner
```## 39.10 Tạo đóng cửa

Khi CPython tạo một đối tượng hàm cho một hàm lồng nhau, nó sẽ gắn các ô đóng nếu hàm lồng nhau cần các biến tự do.

Về mặt khái niệm:```text
outer executes
    create cell for x
    load inner code object
    load closure cell for x
    make function object with closure
    return function object
```Vì:```python
def outer():
    x = 10

    def inner():
        return x

    return inner
```hàm trả về có:```text
inner.__code__
inner.__globals__
inner.__closure__ = (cell_for_x,)
```Bộ đóng cửa đó là như thế nào`inner`thấy`x`.

## 39.11 Closure ghi lại các biến, không phải giá trị

Việc đóng cửa ghi lại các biến thông qua các ô chứ không phải ảnh chụp nhanh các giá trị.```python
def outer():
    x = 1

    def inner():
        return x

    x = 2
    return inner

fn = outer()
print(fn())
```Đầu ra:```text
2
```Hàm bên trong xem nội dung hiện tại của ô. Ô này đã được cập nhật trước đó`outer`đã quay trở lại.

Về mặt khái niệm:```text
cell x initially contains 1
cell x later contains 2
inner reads cell x
```## 39.12 Ô dùng chung

Nhiều chức năng bên trong có thể chia sẻ cùng một ô.```python
def outer():
    x = 0

    def get():
        return x

    def set_value(v):
        nonlocal x
        x = v

    return get, set_value

get, set_value = outer()

print(get())
set_value(10)
print(get())
```Đầu ra:```text
0
10
```Cả hai hàm đều tham chiếu cùng một ô cho`x`.

```text
get.__closure__[0]       -> cell x
set_value.__closure__[0] -> same cell x
```Ô cung cấp liên kết có thể thay đổi được chia sẻ.

## 39,13`nonlocal`

`nonlocal`báo cho trình biên dịch biết rằng phép gán phải nhắm mục tiêu một biến hàm kèm theo, chứ không phải tạo một biến cục bộ mới.```python
def outer():
    x = 0

    def inc():
        nonlocal x
        x += 1
        return x

    return inc
```Không có`nonlocal`, bài tập làm cho`x`địa phương đến`inc`:

```python
def outer():
    x = 0

    def bad():
        x += 1
        return x

    return bad
```Đang gọi`bad()`tăng lên`UnboundLocalError`bởi vì`x += 1`cố gắng đọc địa phương`x`trước khi nó có giá trị.

Với`nonlocal`, CPython thực hiện các hoạt động không quy định đối với ô bên ngoài.

## 39,14`global`vs`nonlocal`

`global`nhắm mục tiêu không gian tên mô-đun.```python
x = 0

def f():
    global x
    x = 10

nonlocalnhắm mục tiêu một phạm vi chức năng kèm theo.```python def outer(): x = 0

def inner():
    nonlocal x
    x = 10

| Tuyên bố | Mục tiêu |
|---|---|
|`global x`| Mô-đun từ điển toàn cầu |
|`nonlocal x`| Phạm vi chức năng kèm theo gần nhất với`x`|
| không khai báo với bài tập | Phạm vi địa phương hiện tại |`nonlocal`không thể nhắm mục tiêu một mô-đun toàn cầu. Nó đòi hỏi một ràng buộc chức năng kèm theo.

## 39.15 Phân tích phạm vi và kết thúc

Trình biên dịch xác định bố cục đóng trong quá trình phân tích bảng ký hiệu.

Đối với mỗi khối mã, nó phân loại tên thành:```text
local
global explicit
global implicit
free
cell
```Ví dụ:```python
def outer():
    x = 1

    def inner():
        return x
```Phân loại:```text
outer:
    x = local, promoted to cell
    inner = local

inner:
    x = free
```Sự phân loại này quyết định hướng dẫn mã byte nào được phát ra.```text
local variable    -> LOAD_FAST / STORE_FAST
global name       -> LOAD_GLOBAL / STORE_GLOBAL
closure variable  -> LOAD_DEREF / STORE_DEREF
```## 39.16 Thời gian đóng cửa

Một ô tồn tại miễn là có thứ gì đó tham chiếu đến nó.

Chủ sở hữu chung:```text
function closure tuple
active frame
generator frame
coroutine frame
another cell or object graph
```Ví dụ:```python
def outer():
    x = [1, 2, 3]

    def inner():
        return x

    return inner

fn = outer()
```Danh sách vẫn còn tồn tại:```text
fn
    __closure__
        cell
            list [1, 2, 3]
```Khi`fn`trở nên không thể truy cập được, bộ dữ liệu đóng và ô có thể được giải phóng và danh sách có thể được giải phóng nếu không có tham chiếu nào khác tồn tại.

## 39.17 Việc đóng cửa thường không giữ được toàn bộ khung hình

Một sự hiểu lầm phổ biến là việc đóng giữ cho toàn bộ khung bên ngoài tồn tại.

Thông thường thì không.```python
def outer():
    a = "captured"
    b = "not captured"

    def inner():
        return a

    return inner
```Hàm trả về cần`a`, nhưng không`b`.

Về mặt khái niệm:```text
inner keeps cell for a
inner does not keep b
outer frame can be destroyed
```Điều này hiệu quả hơn việc bảo toàn toàn bộ khung.

Tuy nhiên, nếu bản thân một đối tượng khung được ghi lại thông qua quá trình xem xét nội tâm thì nó có thể giữ cho tất cả các đối tượng cục bộ còn sống.

## 39.18 Ràng buộc muộn trong vòng lặp

Đóng cửa nắm bắt các biến, không phải giá trị trên mỗi lần lặp.```python
funcs = []

for i in range(3):
    funcs.append(lambda: i)

print([f() for f in funcs])
```Đầu ra:```text
[2, 2, 2]
```Tất cả các lambda đều đóng trên cùng một biến`i`. Sau khi vòng lặp kết thúc,`i`là`2`.

Đây là ràng buộc muộn: giá trị được tra cứu khi hàm chạy chứ không phải khi nó được tạo.

## 39.19 Ghi lại các giá trị hiện tại bằng giá trị mặc định

Sử dụng đối số mặc định để nắm bắt giá trị hiện tại.```python
funcs = []

for i in range(3):
    funcs.append(lambda i=i: i)

print([f() for f in funcs])
```Đầu ra:```text
[0, 1, 2]
```Ở đây, mỗi lambda có giá trị đối số mặc định riêng.

Điều này sử dụng các giá trị mặc định của hàm, không phải các ô đóng.

Về mặt khái niệm:```text
lambda i=0: i
lambda i=1: i
lambda i=2: i
```## 39.20 Đóng cửa trong sự hiểu biết

Sự hiểu biết có phạm vi riêng của chúng, nhưng sự đóng cửa bên trong chúng vẫn có thể cho thấy sự ràng buộc muộn.```python
funcs = [lambda: x for x in range(3)]
print([f() for f in funcs])
```Đầu ra:```text
[2, 2, 2]
```Biến vòng lặp`x`thuộc phạm vi hiểu và tất cả các lambda đều đóng trên cùng một ô đó.

Sử dụng giá trị mặc định để nắm bắt các giá trị trên mỗi lần lặp:```python
funcs = [lambda x=x: x for x in range(3)]
print([f() for f in funcs])
```Đầu ra:```text
[0, 1, 2]
```## 39.21 Đóng cửa và khả năng thay đổi

Một bao đóng có thể tham chiếu một đối tượng có thể thay đổi.```python
def outer():
    xs = []

    def add(x):
        xs.append(x)
        return xs

    return add

add = outer()

print(add(1))
print(add(2))
```Đầu ra:```text
[1]
[1, 2]
```KHÔNG`nonlocal`là cần thiết vì hàm này làm thay đổi đối tượng danh sách. Nó không ràng buộc lại tên`xs`.

Điều này hoạt động:```python
xs.append(x)
```Điều này cần`nonlocal`:

```python
xs = xs + [x]
```Hình thức thứ hai gán lại tên.

## 39.22 Rebound vs Đột biến

So sánh:```python
def outer():
    xs = []

    def add(x):
        xs.append(x)

    return add
```Và:```python
def outer():
    xs = []

    def add(x):
        xs = xs + [x]

    return add
```Cái đầu tiên làm thay đổi đối tượng được tham chiếu bởi`xs`.

Thứ hai gán cho`xs`, do đó trình biên dịch xử lý`xs`như địa phương để`add`trừ khi được khai báo`nonlocal`.

Đảo ngược chính xác:```python
def outer():
    xs = []

    def add(x):
        nonlocal xs
        xs = xs + [x]
        return xs

    return add
```## 39.23 Đóng cửa và các nhà máy hoạt động

Closure thường được sử dụng cho các nhà máy chức năng.```python
def power(exp):
    def apply(x):
        return x ** exp
    return apply

square = power(2)
cube = power(3)

print(square(5))
print(cube(5))
```Đầu ra:```text
25
125
```Mỗi cuộc gọi tới`power`tạo một ô mới cho`exp`.

```text
square closure:
    exp = 2

cube closure:
    exp = 3
```Đối tượng mã cho`apply`có thể được chia sẻ, nhưng các ô đóng khác nhau.

## 39.24 Đóng cửa và trang trí

Người trang trí thường sử dụng các bao đóng.```python
def log_calls(fn):
    def wrapper(*args, **kwargs):
        print("calling", fn.__name__)
        return fn(*args, **kwargs)

    return wrapper
```Sử dụng:```python
@log_calls
def add(a, b):
    return a + b
```Về mặt khái niệm:```python
def add(a, b):
    return a + b

add = log_calls(add)
```Sự trở lại`wrapper`đóng trên bản gốc`fn`.

```text
wrapper.__closure__
    cell for fn -> original add function
```## 39,25 Đóng cửa và trạng thái

Closure có thể lưu trữ trạng thái riêng tư.```python
def counter():
    n = 0

    def inc():
        nonlocal n
        n += 1
        return n

    return inc

c = counter()

print(c())
print(c())
```Đầu ra:```text
1
2
```Nhà nước`n`không được lưu trữ trên một phiên bản. Nó được lưu trữ trong một ô đóng cửa.

Điều này tương tự như một đối tượng nhỏ với một phương thức và trạng thái riêng tư.

## 39.26 Trạng thái đóng so với Trạng thái đối tượng

Phiên bản đóng cửa:```python
def counter():
    n = 0

    def inc():
        nonlocal n
        n += 1
        return n

    return inc
```Phiên bản đối tượng:```python
class Counter:
    def __init__(self):
        self.n = 0

    def inc(self):
        self.n += 1
        return self.n
```So sánh:

| Tính năng | Đóng cửa | Đối tượng |
|---|---|---|
| Lưu trữ nhà nước | Biến ô | Thuộc tính sơ thẩm |
| Nhiều hoạt động | Ít thuận tiện hơn | Tự nhiên |
| Hướng nội | Ít rõ ràng hơn | Rõ ràng hơn |
| Trạng thái gọi lại nhỏ | Phù hợp tốt | Phù hợp tốt |
| Hành vi phong phú | Lúng túng | Tốt hơn |

Đóng cửa là tốt nhất cho trạng thái bị bắt nhỏ. Các lớp học tốt hơn cho các giao thức lớn hơn.

## 39.27 Đóng cửa và Chu kỳ tham chiếu

Việc đóng cửa có thể tham gia vào các chu kỳ.```python
def outer():
    funcs = []

    def inner():
        return funcs

    funcs.append(inner)
    return inner

fn = outer()
```Biểu đồ tham khảo:```text
inner function
    closure cell
        funcs list
            inner function
```Chu trình này có thể được thu thập bởi trình thu gom rác tuần hoàn của CPython khi không thể truy cập được.

Các chu trình liên quan đến khâu quyết toán hoặc nguồn lực bên ngoài cần được quan tâm nhiều hơn.

## 39.28 Đóng và lưu giữ bộ nhớ

Đóng cửa có thể giữ lại các đối tượng lớn.```python
def make_reader():
    data = bytearray(100_000_000)

    def read():
        return data[0]

    return read

reader = make_reader()
```Cái lớn`data`đối tượng vẫn còn sống miễn là`reader`làm.

Chuỗi lưu giữ:```text
reader function
    closure tuple
        cell
            large bytearray
```Nếu việc đóng không còn cần đối tượng lớn nữa, hãy xóa hoặc tránh chụp đối tượng đó.

## 39.29 Tránh vô tình bị bắt

Điều này nắm bắt`self`:

```python
class C:
    def make_callback(self):
        def callback():
            return self.value
        return callback
```Cuộc gọi lại được trả về sẽ giữ cho phiên bản này tồn tại.

Đôi khi điều này được dự định. Đôi khi nó tạo ra sự giữ chân không ngờ tới.

Bạn chỉ có thể nắm bắt giá trị cần thiết:```python
class C:
    def make_callback(self):
        value = self.value

        def callback():
            return value

        return callback
```Bây giờ cuộc gọi lại tiếp tục`value`, không nhất thiết là toàn bộ trường hợp.

## 39.30 Kiểm tra nội dung đóng cửa

sử dụng`__closure__`cẩn thận:```python
def outer():
    x = {"a": 1}

    def inner():
        return x

    return inner

fn = outer()

for name, cell in zip(fn.__code__.co_freevars, fn.__closure__):
    print(name, cell.cell_contents)
```Đầu ra:```text
x {'a': 1}
```Điều này hữu ích cho việc học và gỡ lỗi, nhưng mã sản xuất hiếm khi phụ thuộc vào nội bộ đóng.

## 39.31 Ô trống

Ô đóng có thể trống trong một số trường hợp cạnh.

Các mẫu ví dụ liên quan đến việc xóa có thể tạo ra các ô trống:```python
def outer():
    x = 1

    def inner():
        return x

    del x
    return inner
```Gọi:```python
fn = outer()
fn()
```gây ra lỗi vì ô không còn nội dung nữa.

Truy cập`cell.cell_contents`đối với một ô trống tăng lên`ValueError`.

## 39.32 Đóng cửa và`del`Việc xóa một biến không cục bộ sẽ xóa ô.```python
def outer():
    x = 1

    def delete():
        nonlocal x
        del x

    def read:
        return x

    return read, delete
```Phiên bản đã sửa:```python
def outer():
    x = 1

    def delete():
        nonlocal x
        del x

    def read():
        return x

    return read, delete
```Sử dụng:```python
read, delete = outer()
print(read())
delete()
print(read())
```trận chung kết`read()`tăng vì ô trống.

## 39.33 Đóng cửa và mặc định là khác nhau

Giá trị mặc định được lưu trữ trên đối tượng hàm.```python
def f(x=[]):
    return x
```Các ô đóng cửa được lưu trữ trong`__closure__`.

```python
def outer():
    x = []

    def inner():
        return x

    return inner
```Thanh tra:```python
print(f.__defaults__)
print(outer().__closure__)
```Họ giải quyết các vấn đề khác nhau.

| Cơ chế | Cửa hàng |
|---|---|
| Đối số mặc định | Giá trị được sử dụng khi đối số bị bỏ qua |
| Tế bào đóng cửa | Biến từ phạm vi kèm theo |

Thủ thuật đối số mặc định để liên kết muộn hoạt động vì các giá trị mặc định được đánh giá tại thời điểm tạo hàm.

## 39.34 Đóng cửa và Lambdas`lambda`các hàm tuân theo các quy tắc đóng tương tự như`def`.

```python
def outer():
    x = 10
    return lambda y: x + y

fn = outer()
print(fn(5))
```Đầu ra:```text
15
```Lambda có một biến miễn phí`x`.

```python
print(fn.__code__.co_freevars)
print(fn.__closure__[0].cell_contents)
```Lambda chỉ là một biểu thức hàm thu gọn. Nó không có mô hình đóng cửa đặc biệt.

## 39.35 Đóng cửa và Máy phát điện

Một trình tạo có thể đóng các biến.```python
def outer(limit):
    def gen():
        for i in range(limit):
            yield i

    return gen

make = outer(3)
print(list(make()))
```Chức năng máy phát điện đóng lại`limit`.

Khi`make()`được gọi, nó tạo ra một đối tượng máy phát điện. Đối tượng trình tạo đó có thể truy cập vào ô đóng.

Có hai lớp:```text
generator function
    closure cell for limit

generator object
    suspended frame when running
    references generator function/code/closure state
```## 39.36 Đóng và Coroutine

Các hàm không đồng bộ cũng có thể đóng các biến.```python
def outer(delay):
    async def wait_then_return(value):
        await sleep(delay)
        return value

    return wait_then_return
```Chức năng async đóng lại`delay`.

Việc gọi nó sẽ tạo ra một đối tượng coroutine. Trong khi bị treo, coroutine vẫn giữ trạng thái khung và hàm mang các ô đóng.

Điều này có thể giữ lại các đối tượng bị bắt trong thời gian chờ đợi.

## 39.37 Hiệu suất đóng cửa

Truy cập một biến đóng thường chậm hơn so với truy cập cục bộ nhanh.

Địa phương nhanh:```text
LOAD_FAST
```Biến đóng cửa:```text
LOAD_DEREF

LOAD_DEREFphải đọc qua một ô.

Trong hầu hết các mã, chi phí này là nhỏ. Trong các vòng lặp rất nóng, ràng buộc cục bộ có thể quan trọng.

Ví dụ:python def outer(value): def inner(xs): v = value total = 0 for x in xs: total += x + v return total return inner Đây,vlà một người địa phương nhanh chóng bên tronginner, trong khivalueđược đọc từ một ô một lần.

39.38 Gỡ lỗi đóng cửa

Khi gỡ lỗi hành vi đóng, hãy kiểm tra:python fn.__code__.co_freevars fn.__closure__ cell.cell_contents Ví dụ:```python def outer(): x = 1 y = 2

def inner():
    return x + y

return inner

fn = outer()

for name, cell in zip(fn.code.co_freevars, fn.closure): print(name, cell.cell_contents)


## 39.39 Những hiểu lầm thường gặp

| Hiểu lầm | Đúng mẫu |
|---|---|
| Giá trị sao chép đóng cửa | Họ nắm bắt các biến thông qua các ô |
| Việc đóng cửa giữ cho toàn bộ khung bên ngoài tồn tại | Thông thường nó chỉ giữ những ô cần thiết |
|`nonlocal`có nghĩa là toàn cầu | Nó nhắm đến một phạm vi chức năng kèm theo |
| Thay đổi nhu cầu danh sách đã chụp`nonlocal`| Chỉ cần đảo ngược tên`nonlocal`|
| Lambdas có quy tắc đóng khác nhau | Lambda và`def`sử dụng cùng một mô hình đóng cửa |
| Việc đóng vòng lặp nắm bắt từng giá trị lặp lại | Họ nắm bắt biến vòng lặp |
| Mặc định và đóng cửa giống nhau | Giá trị lưu trữ mặc định; đóng cửa các tế bào lưu trữ |
| Các biến đóng cửa là các biến cục bộ nhanh | Chúng được truy cập thông qua các ô |

##39.40 Chiến lược đọc

Bắt đầu với:```python
def outer():
    x = 10

    def inner():
        return x

    return inner
```Thanh tra:```python
import dis

fn = outer()

print(outer.__code__.co_cellvars)
print(fn.__code__.co_freevars)
print(fn.__closure__)
print(fn.__closure__[0].cell_contents)

dis.dis(outer)
dis.dis(fn)
```Sau đó kiểm tra:```text
nonlocal rebinding
multiple inner functions sharing one variable
lambda inside loops
default-argument capture
closures holding large objects
closures inside generators
closures inside async functions
```Đối với mỗi trường hợp, theo dõi:```text
which scope binds the name
whether the name becomes a cell variable
which inner function sees it as a free variable
which function owns the closure tuple
how long the cell remains alive
```## 39.41 Tóm tắt chương

Các bao đóng là cơ chế của CPython để cho phép các hàm lồng nhau truy cập các biến từ phạm vi hàm kèm theo. Khi một biến cục bộ bên ngoài được hàm bên trong sử dụng, CPython sẽ lưu trữ biến đó trong một ô. Hàm bên trong mang một bộ đóng chứa các ô cần thiết.

Mô hình cốt lõi là:```text
outer function local used by inner function
    ↓
local becomes a cell variable
    ↓
inner function records it as a free variable
    ↓
function object stores closure tuple
    ↓
cell remains alive after outer frame returns
    ↓
inner function reads or writes cell contents
```Các bao đóng giải thích các hàm lồng nhau, trình trang trí, nhà máy hàm, trạng thái gọi lại,`nonlocal`, liên kết muộn trong các vòng lặp và duy trì bộ nhớ thông qua các biến được ghi lại.