35. Máy phát điện
#35. Máy phát điện
Máy phát điện là chức năng có thể tiếp tục. Một hàm bình thường bắt đầu, chạy và kết thúc với một giá trị trả về. Trình tạo có thể khởi động, tạo ra một giá trị, tạm dừng khung của nó, sau đó tiếp tục từ cùng một vị trí lệnh, tạo ra một giá trị khác và lặp lại cho đến khi kết thúc.
Hàm tạo là bất kỳ phần thân hàm nào có chứayield.
def numbers():
yield 1
yield 2
yield 3
```Việc gọi hàm này không chạy phần thân ngay lập tức.```python id="oyb9ik"
g = numbers()
```Cuộc gọi tạo ra một đối tượng máy phát điện. Phần thân khởi động khi máy phát điện được nối lại:```python id="9sgmxp"
print(next(g))
print(next(g))
print(next(g))
```Đầu ra:```text id="wj3i3f"
1
2
3
```Sau giá trị cuối cùng, sơ yếu lý lịch tiếp theo sẽ tăng`StopIteration`.
## 35.1 Hàm tạo và đối tượng trình tạo
Hàm tạo là hàm có thể gọi được xác định bằng`def`.
Đối tượng trình tạo là trình vòng lặp có thể tiếp tục được trả về khi hàm tạo được gọi.```python id="nckwxg"
def gen():
yield 1
print(gen)
print(gen())
```Về mặt khái niệm:```text id="hfh6hz"
gen
function object
gen()
generator object
suspended execution state
code object
frame or frame-like state
```Điều này khác với một chức năng thông thường:```python id="wsroz3"
def f():
return 1
f()
```Đang gọi`f`chạy cơ thể ngay lập tức và trở về`1`.
Đang gọi`gen`trả về một đối tượng mà sau này có thể chạy phần thân.
## 35,2`yield`các`yield`biểu thức tạo ra một giá trị cho trình gọi của trình tạo và tạm dừng thực thi.```python id="cwhj6e"
def gen():
x = 10
yield x
x = 20
yield x
```Trình tự thực hiện:```text id="jvg4tx"
create generator object
resume generator
x = 10
yield 10
suspend
resume generator
x = 20
yield 20
suspend
resume generator
finish function
raise StopIteration
```Biến cục bộ`x`tồn tại qua hệ thống treo vì trình tạo vẫn giữ trạng thái thực thi.
## 35.3 Generator là Iterator
Đối tượng trình tạo thực hiện giao thức lặp.```python id="mtd1nu"
g = numbers()
print(iter(g) is g)
print(next(g))
```Một máy phát điện có:```text id="vlpm43"
__iter__
__next__
send
throw
close
```các`for`vòng lặp hoạt động vì các trình tạo là các trình vòng lặp:```python id="9z52dp"
for x in numbers():
print(x)
```Về mặt khái niệm:```text id="rdvvmh"
it = iter(numbers())
while True:
try:
x = next(it)
except StopIteration:
break
print(x)
```## 35.4 Khung máy phát điện
Trình tạo phải duy trì trạng thái thực thi giữa các lần tiếp tục.
Trạng thái đó bao gồm:```text id="9ta9ii"
code object
instruction position
local variables
value stack
exception state
closure cells
running state
finished state
```Ví dụ:```python id="h463cg"
def gen():
a = 1
b = 2
yield a
yield b
```Sau lần đầu tiên`yield`, máy phát điện phải nhớ:```text id="oj7swc"
a = 1
b = 2
next instruction is after first yield
```Đây là lý do tại sao máy phát điện được kết nối chặt chẽ với các khung.
## 35.5 Khung hàm thông thường so với Khung tạo
Khung chức năng bình thường thường biến mất sau khi quay lại.```python id="lzuq79"
def f():
x = 1
return x
```Sau đó`f()`trở lại, khung có thể bị xóa.
Khung máy phát điện vẫn tồn tại trong khi bị treo.```python id="6zyz25"
def gen():
x = 1
yield x
```Sau đó`next(gen())`đạt tới`yield`, khung không thể bị xóa vì nó có thể tiếp tục lại sau đó.
| Tính năng | Chức năng bình thường | Máy phát điện |
|---|---|---|
| Gọi chạy body liền | Có | Không |
| Có thể đình chỉ | Không | Có |
| Giữ người dân địa phương sau khi nhượng bộ | Không | Có |
| Trả về một kết quả cuối cùng | Có | Kết quả cuối cùng trở thành`StopIteration.value`|
| Triển khai giao thức lặp | Không | Có |
| Tuổi thọ khung hình | Thời lượng cuộc gọi thường | Cho đến khi hoàn thành hoặc đóng |
## 35,6`next()`
`next(g)`nối lại một máy phát điện.```python id="07krhu"
def gen():
yield "a"
yield "b"
g = gen()
print(next(g))
print(next(g))
```Thực thi:```text id="tuypws"
next(g)
resume at start
yield "a"
return "a" to caller
next(g)
resume after first yield
yield "b"
return "b" to caller
```Đối tượng trình tạo ghi lại vị trí lệnh giữa các cuộc gọi.
## 35.7 Hoàn thành và`StopIteration`Khi một máy phát điện kết thúc, nó sẽ tăng`StopIteration`.
```python id="suekt2"
def gen():
yield 1
g = gen()
print(next(g))
print(next(g))
```thứ hai`next(g)`tăng lên`StopIteration`.
Một máy phát điện có thể kết thúc bằng cách:```text id="6n5ndu"
falling off the end
executing return
raising an exception
being closed
```Rơi khỏi cuối cùng tương đương với việc quay trở lại`None`.
```python id="51z1x1"
def gen():
yield 1
```Sau đó`yield 1`, hàm đi đến cuối. Sơ yếu lý lịch tiếp theo tăng`StopIteration`.
## 35,8`return`trong máy phát điện
Máy phát điện có thể sử dụng`return value`.
```python id="lbrk4s"
def gen():
yield 1
return 99
```Giá trị trả về trở thành`value`thuộc tính của`StopIteration`.
```python id="tyi2fu"
g = gen()
print(next(g))
try:
next(g)
except StopIteration as exc:
print(exc.value)
```Đầu ra:```text id="dzo3b2"
1
99
```MỘT`for`vòng lặp bỏ qua trận chung kết`StopIteration.value`.
## 35,9`yield`là một biểu thức`yield`có thể nhận được một giá trị thông qua`send`.
```python id="vpynq0"
def gen():
x = yield "ready"
yield x
```Sử dụng:```python id="5cofsb"
g = gen()
print(next(g))
print(g.send(42))
```Thực thi:```text id="fafbmz"
next(g)
runs until yield "ready"
returns "ready"
g.send(42)
resumes generator
yield expression evaluates to 42
x = 42
yield x
```Vì thế`yield`cả hai đều gửi một giá trị ra và có thể nhận lại một giá trị.
## 35.10 Khởi động máy phát điện bằng`send`Trình tạo mới được tạo chưa đạt đến lần đầu tiên`yield`.
Vì vậy, sơ yếu lý lịch đầu tiên phải sử dụng`next(g)`hoặc`g.send(None)`.
```python id="ltjgnn"
g = gen()
g.send(None)
```Gửi một tin nhắn không phải`None`giá trị cho trình tạo mới khởi động là một lỗi vì không có giá trị nào bị treo`yield`biểu hiện để nhận được nó.```python id="w4yvh2"
g = gen()
g.send(42)
```Điều này làm tăng`TypeError`.
## 35.11`throw`
`throw`tiếp tục một trình tạo bằng cách đưa ra một ngoại lệ tại thời điểm bị treo`yield`.
```python id="1i4m5p"
def gen():
try:
yield "ready"
except ValueError:
yield "handled"
g = gen()
print(next(g))
print(g.throw(ValueError))
```Thực thi:```text id="h3sq71"
next(g)
yield "ready"
g.throw(ValueError)
resume at yield by raising ValueError
except ValueError catches it
yield "handled"
throwcho phép người gọi đưa một ngoại lệ vào trình tạo.
35,12close
closeyêu cầu một máy phát điện chấm dứt.```python id="4p6xj9"
def gen():
try:
yield 1
finally:
print("cleanup")
g = gen()
next(g)
g.close()
```Đóng tiêmGeneratorExitvào máy phát điện. cácfinallykhối chạy.
Trình tạo không được mang lại giá trị bình thường khi đóng. Nếu đúng như vậy, CPython sẽ tăngRuntimeError.
def bad():
try:
yield 1
finally:
yield 2
```Đang gọi`close()`sau sản lượng đầu tiên gây ra lỗi do máy phát điện sinh ra trong quá trình đóng.
## 35.13 Trạng thái máy phát điện
Một máy phát điện có thể ở một số trạng thái:```text id="spbcno"
created
running
suspended
closed
```sử dụng`inspect`:
```python id="9b2nga"
import inspect
def gen():
yield 1
g = gen()
print(inspect.getgeneratorstate(g))
next(g)
print(inspect.getgeneratorstate(g))
try:
next(g)
except StopIteration:
pass
print(inspect.getgeneratorstate(g))
```Các trạng thái điển hình:```text id="51wa06"
GEN_CREATED
GEN_SUSPENDED
GEN_CLOSED
```Máy phát điện không thể tiếp tục hoạt động khi đang chạy.
## 35.14 Bảo vệ tái truy cập
Máy phát điện không thể được nhập lại.```python id="h0ak06"
def gen():
yield next(g)
g = gen()
next(g)
```Điều này cố gắng để tiếp tục`g`trong khi`g`đang chạy rồi. CPython gây ra lỗi.
Trình tạo có cờ đang chạy để tránh làm hỏng trạng thái khung của nó.
Về mặt khái niệm:```text id="lm6k96"
if generator is already executing:
raise ValueError or RuntimeError depending on context
```Điều này bảo vệ khung treo và ngăn xếp.
## 35.15 Mã byte của trình tạo
Hàm tạo sẽ biên dịch thành đối tượng mã được đánh dấu là trình tạo.```python id="af0twx"
def gen():
yield 1
```Việc gọi hàm sẽ tạo một đối tượng trình tạo thay vì thực thi khung để hoàn thành.
Một chuỗi hướng dẫn khái niệm:```text id="n6w1a4"
LOAD_CONST 1
YIELD_VALUE
RESUME
LOAD_CONST None
RETURN_VALUE
```Mã byte chính xác thay đổi tùy theo phiên bản Python.
Hướng dẫn chính là`YIELD_VALUE`, sẽ gửi một giá trị cho người gọi và tạm dừng thực thi.
## 35,16`yield from`
`yield from`ủy quyền cho một trình vòng lặp hoặc trình tạo khác.```python id="uxya55"
def outer():
yield from inner()
```Nó gần tương đương với:```python id="37kb2t"
for value in inner():
yield value
```Nhưng nó cũng chuyển tiếp:```text id="hq74n9"
send
throw
close
StopIteration.value
```Điều này làm cho`yield from`mạnh mẽ hơn một vòng lặp đơn giản.
## 35.17 Đoàn Với`yield from`Ví dụ:```python id="9ui4p5"
def inner():
yield 1
yield 2
return 99
def outer():
result = yield from inner()
yield result
print(list(outer()))
```Đầu ra:```text id="4ncupl"
[1, 2, 99]
```Giá trị trả về của`inner`trở thành kết quả của`yield from`biểu hiện trong`outer`.
Về mặt khái niệm:```text id="02qghi"
outer delegates to inner
inner yields 1
outer yields 1 to caller
inner yields 2
outer yields 2 to caller
inner returns 99 via StopIteration.value
yield from expression evaluates to 99
outer yields 99
```## 35,18`yield from`Và`send`
`yield from`chuyển tiếp các giá trị được gửi bởi người gọi.```python id="nfxupw"
def inner():
x = yield "inner ready"
yield x
def outer():
yield from inner()
g = outer()
print(next(g))
print(g.send(42))
```Đầu ra:```text id="9lagzv"
inner ready
42
```các`send(42)`đạt tới mức bị đình chỉ`yield`bên trong`inner`.
## 35,19`yield from`và Ngoại lệ`yield from`chuyển tiếp ngoại lệ quá.```python id="nxzr78"
def inner():
try:
yield "ready"
except ValueError:
yield "handled"
def outer():
yield from inner()
g = outer()
print(next(g))
print(g.throw(ValueError))
```Ngoại lệ được ném vào`inner`, không được xử lý trực tiếp bởi`outer`, trừ khi việc ủy quyền kết thúc hoặc bên trong thiếu trình xử lý thích hợp.
## 35.20 Trình tạo biểu thức
Biểu thức trình tạo tạo ra một đối tượng giống như trình tạo.```python id="gkiij5"
squares = (x * x for x in range(10))
```Nó lười biếng. Các giá trị được tính toán theo yêu cầu.```python id="ey0d2q"
print(next(squares))
print(next(squares))
```Một biểu thức trình tạo có phạm vi giống như hàm ẩn của riêng nó.```python id="wkic1b"
x = 100
g = (x for x in range(3))
print(x)
```Bên ngoài`x`còn lại`100`.
## 35.21 Hiểu danh sách và biểu thức trình tạo
Việc hiểu danh sách sẽ xây dựng toàn bộ danh sách ngay lập tức.```python id="h6kqv5"
xs = [x * x for x in range(10)]
```Một biểu thức trình tạo tạo ra các giá trị một cách lười biếng.```python id="j8cabg"
g = (x * x for x in range(10))
```| Tính năng | Danh sách hiểu | Biểu thức máy phát điện |
|---|---|---|
| Đánh giá | Háo hức | Lười biếng |
| Kết quả | Danh sách | Trình lặp giống như máy phát điện |
| Ký ức | Lưu trữ tất cả kết quả | Lưu trữ trạng thái thực thi |
| Tái sử dụng | Có, danh sách có thể lặp lại nhiều lần | Không, máy phát điện đã hết |
| Phạm vi | Phạm vi hiểu riêng | Phạm vi máy phát điện riêng |
## 35.22 Lặp lại một lần
Trình tạo là trình lặp một lần.```python id="mj534b"
g = (x for x in range(3))
print(list(g))
print(list(g))
```Đầu ra:```text id="qbh3cb"
[0, 1, 2]
[]
```Sau khi cạn kiệt, máy phát điện vẫn cạn kiệt.
Điều này khác với một vùng chứa như danh sách:```python id="b4xrxl"
xs = [0, 1, 2]
print(list(xs))
print(list(xs))
```Một danh sách sẽ tạo một trình vòng lặp mới mỗi lần. Một trình tạo là trình vòng lặp của chính nó.
## 35.23 Đánh giá lười biếng
Máy phát điện tính toán các giá trị theo yêu cầu.```python id="c1xawr"
def read_lines(path):
with open(path) as f:
for line in f:
yield line.rstrip("\n")
```Điều này không đọc toàn bộ tập tin vào bộ nhớ. Nó đọc và cho ra từng dòng một.
Thực thi lười biếng rất hữu ích cho:```text id="dhu35c"
large files
streams
pipelines
infinite sequences
expensive computations
early stopping
```Ví dụ:```python id="zb1yck"
def count():
n = 0
while True:
yield n
n += 1
```Trình tạo này đại diện cho một chuỗi vô hạn.
## 35.24 Kiểu đường ống
Máy phát điện sáng tác tự nhiên.```python id="j6opqr"
def numbers(path):
with open(path) as f:
for line in f:
yield int(line)
def positive(xs):
for x in xs:
if x > 0:
yield x
def squared(xs):
for x in xs:
yield x * x
```Sử dụng:```python id="1rj6pt"
pipeline = squared(positive(numbers("data.txt")))
for x in pipeline:
print(x)
```Mỗi giai đoạn kéo từ giai đoạn trước. Không cần có danh sách trung gian đầy đủ.
## 35.25 Dọn dẹp máy phát điện
Trình tạo quản lý tài nguyên nên sử dụng`try/finally`hoặc người quản lý bối cảnh.```python id="8m79yy"
def lines(path):
f = open(path)
try:
for line in f:
yield line
finally:
f.close()
```Nếu máy phát điện bị đóng trước khi cạn kiệt,`finally`khối chạy.
Một hình thức tốt hơn:```python id="yyr3qh"
def lines(path):
with open(path) as f:
for line in f:
yield line
```các`with`câu lệnh được biên dịch thành logic dọn dẹp hoạt động khi đóng trình tạo.
## 35.26 Trình tạo và rò rỉ tài nguyên
Một máy phát điện bị treo có thể giữ cho tài nguyên tồn tại.```python id="2s3f45"
def gen():
f = open("data.txt")
yield f.readline()
f.close()
```Nếu lệnh gọi dừng sau giá trị đầu tiên và không bao giờ đóng trình tạo, tệp có thể vẫn mở cho đến khi trình tạo được thu thập.
Sử dụng`with`hoặc đóng một cách rõ ràng:```python id="lz764o"
g = gen()
next(g)
g.close()
```Quyền sở hữu tài nguyên phải rõ ràng trong mã trình tạo.
## 35.27 Trình tạo và ngoại lệ trong`finally`Nếu mã dọn dẹp tăng lên, ngoại lệ đó sẽ lan truyền trong quá trình đóng hoặc hoàn thiện trình tạo.```python id="xdp983"
def gen():
try:
yield 1
finally:
raise RuntimeError("cleanup failed")
```Đang gọi`g.close()`sau khi khởi động máy phát điện tăng`RuntimeError`.
Các ngoại lệ về thời gian quyết toán có thể được báo cáo là không thể xử lý được nếu không có ngữ cảnh người gọi thông thường.
## 35.28 Duy trì bộ nhớ máy phát điện
Một trình tạo bị treo giữ cho các biến cục bộ của nó tồn tại.```python id="7evyaw"
def gen():
data = bytearray(100_000_000)
yield 1
return len(data)
g = gen()
next(g)
```Sau lần đầu tiên`yield`, `data`vẫn còn sống vì máy phát điện có thể tiếp tục và sử dụng nó.
Chuỗi lưu giữ:```text id="a05d71"
generator object
suspended frame
local data
```Để giải phóng bộ nhớ, hãy xả hoặc đóng trình tạo hoặc tránh giữ lại các cục bộ lớn trên sản lượng.
## 35.29 Dọn dẹp các địa phương lớn
Nếu không cần một vật lớn sau khi nhường đường, hãy dọn sạch nó trước khi nhường đường hoặc trước khi tạm dừng kéo dài.```python id="mdphyz"
def gen():
data = bytearray(100_000_000)
result = process(data)
data = None
yield result
```Điều này cho phép vật thể lớn được thả ra trước khi bị treo.
Khung máy phát điện vẫn tồn tại nhưng không còn tham chiếu nữa`data`.
## 35.30 Máy phát điện và`for`Vòng lặp
Một máy phát điện thường xuất hiện bên trong một`for`vòng lặp:```python id="ywz49k"
for value in gen():
use(value)
```Vòng lặp liên tục gọi`next()`cho đến khi`StopIteration`.
Nếu vòng lặp thoát sớm với`break`, đối tượng trình tạo có thể không thể truy cập được và sau đó bị đóng. Nhưng việc dựa vào quyết toán ngay lập tức là tùy vào từng trường hợp thực hiện cụ thể. Sử dụng trình quản lý bối cảnh khi vấn đề về thời gian dọn dẹp.
## 35.31 Trình quản lý bối cảnh dựa trên trình tạo
các`contextlib.contextmanager`trang trí biến trình tạo thành trình quản lý bối cảnh.```python id="vtcps7"
from contextlib import contextmanager
@contextmanager
def managed():
print("enter")
try:
yield "value"
finally:
print("exit")
with managed() as value:
print(value)
```Máy phát điện mang lại chính xác một lần.
Về mặt khái niệm:```text id="2vq7qx"
__enter__
run generator until yield
return yielded value
__exit__
resume generator to run cleanup
```Nếu phần thân tăng lên, ngoại lệ sẽ được ném vào trình tạo tại`yield`.
## 35.32 Các phương thức giao thức của trình tạo
Các đối tượng trình tạo hỗ trợ các phương thức quan trọng sau:
| Phương pháp | Ý nghĩa |
|---|---|
|`__next__()`| Tiếp tục và gửi`None` |
| `send(value)`| Tiếp tục và gửi giá trị vào hiện tại`yield` |
| `throw(exc)`| Tiếp tục bằng cách nâng cao ngoại lệ hiện tại`yield` |
| `close()`| tiêm`GeneratorExit`và đóng |
|`__iter__()`| Tự quay về |`next(g)`cuộc gọi`g.__next__()`.
`g.__next__()`tương đương với`g.send(None)`đối với máy phát điện treo.
## 35.33 Thuộc tính của Trình tạo
Các đối tượng trình tạo hiển thị các thuộc tính hữu ích.```python id="yu9p0l"
def gen():
yield 1
g = gen()
print(g.gi_code)
print(g.gi_frame)
print(g.gi_running)
```Các thuộc tính chung bao gồm:
| Thuộc tính | Ý nghĩa |
|---|---|
|`gi_code`| Đối tượng mã |
|`gi_frame`| khung hoặc`None`khi đóng cửa |
|`gi_running`| Cho dù hiện đang thực thi |
|`gi_yieldfrom`| Trình lặp được ủy quyền hiện tại cho`yield from`, nếu có |
Đây là các móc nội quan cấp CPython và có thể tiết lộ chi tiết triển khai.
## 35.34 Trình tạo và truy nguyên
Nếu trình tạo đưa ra một ngoại lệ, thì truy nguyên sẽ bao gồm khung trình tạo.```python id="u2d2xz"
def gen():
yield 1
1 / 0
g = gen()
next(g)
next(g)
```thứ hai`next(g)`tiếp tục bên trong máy phát điện và tăng lên`ZeroDivisionError`.
Truy nguyên trỏ đến dòng bị lỗi bên trong`gen`.
Khung trình tạo là một phần của truy nguyên giống như bất kỳ khung Python nào khác.
## 35.35 Máy phát điện và`StopIteration`Chuyển đổi
Bên trong máy phát điện, vô tình`StopIteration`là nguy hiểm.```python id="mmdpxy"
def gen():
next(iter([]))
yield 1
```nội bộ`next(iter([]))`tăng lên`StopIteration`.
Python hiện đại biến điều này thành`RuntimeError`khi nó thoát ra khỏi thân máy phát điện. Điều này ngăn chặn việc chấm dứt ngẫu nhiên trông giống như việc hoàn thành trình tạo bình thường.
Mã đúng sẽ nắm bắt rõ ràng nếu được mong đợi:```python id="tmjsqq"
def gen():
try:
value = next(iter([]))
except StopIteration:
return
yield value
```## 35.36 Trình tạo và Async
Trình tạo có liên quan nhưng khác với trình tạo coroutine và trình tạo không đồng bộ.
| Xây dựng | Từ khóa | Sản xuất |
|---|---|---|
| Máy phát điện |`def`với`yield`| Đối tượng máy phát điện |
| Coroutine |`async def`| Đối tượng Coroutine |
| Trình tạo không đồng bộ |`async def`với`yield`| Đối tượng trình tạo không đồng bộ |
Một máy phát điện bình thường sử dụng`next`, `send`, `throw`, Và`close`.
Một coroutine sử dụng`await`và lập kế hoạch vòng lặp sự kiện.
Trình tạo không đồng bộ sử dụng`async for`Và`anext`.
Họ chia sẻ ý tưởng về việc thực hiện có thể tiếp tục nhưng khác nhau về giao thức.
## 35.37 Mô hình thực thi CPython
Ở cấp độ CPython, trình tạo là một đối tượng heap sở hữu trạng thái thực thi bị treo.
Về mặt khái niệm:```text id="y0j37u"
PyGenObject
code object
frame or interpreter frame state
name and qualname
exception state
running flag
weakrefs
yield-from target
```Cấu trúc chính xác thay đổi giữa các phiên bản, nhưng các trường khái niệm vẫn còn.
Khi được tiếp tục:```text id="zrin3w"
check generator is not closed
check generator is not already running
mark running
enter evaluation loop with saved frame
run until yield, return, or exception
save frame state if yielded
clear frame state if completed
mark not running
return yielded value or propagate exception
```## 35,38`YIELD_VALUE`các`YIELD_VALUE`hướng dẫn là hoạt động mã byte chính.
Về mặt khái niệm:```text id="bpj8tu"
value = pop stack
save current frame position
return value to generator caller
mark generator suspended
```Khi được tiếp tục, việc thực thi tiếp tục sau lệnh lợi nhuận.
Giá trị mang lại không phải là giá trị trả về cuối cùng của hàm. Nó là kết quả trung gian được cung cấp bởi giao thức lặp.
## 35,39`SEND`và Phái đoàn
Mã byte hiện đại có hỗ trợ cụ thể để gửi giá trị vào trình tạo, coroutine và đường dẫn ủy nhiệm.
Về mặt khái niệm, một hoạt động gửi:```text id="z95s95"
resume suspended iterator/coroutine
send value or None
receive yielded value, return value, or exception
yield fromVàawaitcả hai đều phụ thuộc vào việc gửi vào một đối tượng có thể tiếp tục khác.
Đây là cách kết nối các phép tính có thể tiếp tục lồng nhau mà không cần viết vòng lặp đầy đủ theo cách thủ công.
35.40 Những hiểu lầm phổ biến
| Hiểu lầm | Đúng mẫu |
|---|---|
| Gọi một hàm tạo sẽ chạy nó | Nó tạo ra một đối tượng máy phát điện |
yieldcũng giống nhưreturn |
yieldđình chỉ;returnhoàn thành |
| Một máy phát điện có thể được tái sử dụng sau khi cạn kiệt | Đó là một lần |
| Trình tạo lưu trữ tất cả các giá trị | Nó lưu trữ trạng thái thực thi và tính toán một cách lười biếng |
yield fromchỉ là cú pháp cho một vòng lặp |
Nó cũng chuyển tiếp giá trị gửi, ném, đóng và trả về |
| Máy phát điện giải phóng người dân địa phương sau mỗi lần sản lượng | Người dân địa phương vẫn còn sống trong khi bị đình chỉ |
MỘTforvòng lặp nhìn thấyStopIteration.value |
Nó bỏ qua giá trị |
close()chỉ là xóa |
Nó tiêmGeneratorExitvà chạy dọn dẹp |
35.41 Chiến lược đọc
Bắt đầu với một máy phát điện nhỏ:python id="wby2x2" def gen(): x = 1 yield x x = 2 yield x Thanh tra:```python id="hvaeen"
import dis
import inspect
g = gen()
print(inspect.getgeneratorstate(g)) dis.dis(gen)
print(next(g)) print(inspect.getgeneratorstate(g)) print(g.gi_frame.f_locals)
print(next(g))
print(inspect.getgeneratorstate(g))
print(g.gi_frame.f_locals)
Sau đó nghiên cứu:text id="tf7v1e"
return value
send
throw
close
yield from
try/finally
generator expressions
contextlib.contextmanager
Đối với mỗi trường hợp, theo dõi:text id="a4iald"
when the body starts
where execution suspends
which locals remain alive
what resumes execution
what exception or value crosses the boundary
when the frame is cleared
Trình tạo là các hàm có thể tiếp tục được triển khai dưới dạng đối tượng trình vòng lặp với trạng thái thực thi đã lưu. Lệnh gọi hàm tạo sẽ tạo một đối tượng trình tạo. Thân máy chỉ chạy khi máy phát điện được nối lại bằng cách`next`, `send`, `throw`, hoặc`close`.
Mô hình cốt lõi là:```text id="4a89xx"
generator function call
↓
create generator object with suspended frame
↓
next/send resumes frame
↓
yield returns value and suspends frame
↓
resume later from same point
↓
return or end raises StopIteration
```Trình tạo kết nối việc thực thi mã byte, khung, xử lý ngoại lệ, lặp lại, đánh giá lười biếng, tuổi thọ bộ nhớ và ngữ nghĩa dọn dẹp.
Chúng là một trong những ví dụ rõ ràng nhất về việc CPython coi trạng thái thực thi là một đối tượng.