30. Hướng dẫn mã byte
30. Hướng dẫn mã byte
Hướng dẫn mã byte là các hoạt động được thực hiện bởi vòng đánh giá CPython. Chúng là dạng mã Python nhỏ gọn, ở cấp độ trình thông dịch sau khi phân tích cú pháp, xây dựng AST, phân tích ký hiệu và biên dịch.
Một hàm Python như:```python id="ya54lv" def add(a, b): return a + b
Bạn có thể kiểm tra luồng đó bằng`dis`:
```python id="7yf46p"
import dis
def add(a, b):
return a + b
dis.dis(add)
```Đầu ra phụ thuộc vào phiên bản Python, nhưng nó thường hiển thị các hướng dẫn như:```text id="ot1vx4"
LOAD_FAST
LOAD_FAST
BINARY_OP
RETURN_VALUE
```Những hướng dẫn đó là từ vựng của máy ảo CPython.
## 30.1 Lệnh Bytecode là gì
Lệnh mã byte yêu cầu trình thông dịch thực hiện một thao tác nhỏ.
Ví dụ:```text id="4m3y16"
load a local variable
load a constant
store into a local variable
perform binary addition
call a function
jump to another instruction
return from a frame
raise an exception
build a list
load an attribute
```Một lệnh thường có hai phần:```text id="3pmt1a"
opcode
operand
```Opcode cho biết phải làm gì.
Toán hạng cung cấp một đối số số nguyên nhỏ khi cần thiết.
Ví dụ:```text id="7i5m11"
LOAD_FAST 0
```có nghĩa:```text id="ssxj7c"
load fast local variable at slot 0
```Và:```text id="po4eiq"
LOAD_CONST 1
```có nghĩa:```text id="056i57"
load constant at index 1 in the code object's constants table
```Một số hướng dẫn không có toán hạng có ý nghĩa. Một số có toán hạng mà ý nghĩa của nó phụ thuộc hoàn toàn vào opcode.
## 30.2 Mã byte tồn tại trong các đối tượng mã
Bytecode thuộc về một đối tượng mã.
Một đối tượng hàm trỏ đến một đối tượng mã:```python id="3zs8ni"
def f(x):
return x + 1
print(f.__code__)
```Đối tượng mã chứa luồng lệnh và các bảng được sử dụng bởi các lệnh.
Dữ liệu đối tượng mã quan trọng bao gồm:
| Lĩnh vực | Mục đích |
|---|---|
|`co_code`| Biểu diễn mã byte tiếp xúc với Python |
|`co_consts`| Các hằng số được sử dụng bởi`LOAD_CONST` |
| `co_names`| Tên được sử dụng bởi các hoạt động toàn cầu, thuộc tính và nhập |
|`co_varnames`| Tên biến cục bộ nhanh |
|`co_freevars`| Biến miễn phí từ phạm vi bên ngoài |
|`co_cellvars`| Người dân địa phương được nắm bắt bởi các chức năng bên trong |
|`co_stacksize`| Độ sâu ngăn xếp giá trị tối đa |
|`co_flags`| Cờ thực thi |
|`co_filename`| Tên tệp nguồn |
|`co_name`| Tên đối tượng mã |
|`co_qualname`| Tên đủ điều kiện |
| bảng ngoại lệ | Siêu dữ liệu xử lý ngoại lệ |
| bảng dòng | Siêu dữ liệu vị trí nguồn |
Luồng mã byte nhỏ gọn vì nó không lưu trữ trực tiếp tên đầy đủ, hằng hoặc con trỏ đối tượng. Nó lưu trữ các chỉ mục số nguyên vào các bảng này.
## 30.3 Hướng dẫn Tham khảo Bảng
Hãy xem xét:```python id="p7suvh"
def f(x):
return x + 10
```Hằng số`10`được lưu trữ trong bảng hằng số của đối tượng mã.
Tên địa phương`x`được lưu trữ trong bảng biến cục bộ.
Luồng hướng dẫn đề cập đến chúng theo chỉ mục.
Về mặt khái niệm:```text id="d913xq"
co_consts:
[None, 10]
co_varnames:
["x"]
bytecode:
LOAD_FAST 0
LOAD_CONST 1
BINARY_OP +
RETURN_VALUE
```Thiết kế này giữ cho hướng dẫn nhỏ.
Thay vì lưu trữ chuỗi`"x"`trong mọi lệnh truy cập cục bộ, CPython lưu trữ số vị trí`0`.
Thay vì lưu trữ đối tượng`10`ngay bên trong lệnh, CPython lưu trữ chỉ số không đổi`1`.
## 30.4 Tháo gỡ
các`dis`mô-đun chuyển đổi mã byte thành dạng có thể đọc được.```python id="45j36i"
import dis
def f(a, b):
c = a + b
return c
dis.dis(f)
```Việc tháo dỡ thường bao gồm:```text id="zxtwgj"
source line number
bytecode offset
opcode name
operand
resolved operand meaning
jump target markers
cache entries, depending on options and version
```Hình dạng ví dụ:```text id="pely0k"
3 0 RESUME 0
2 LOAD_FAST 0 (a)
4 LOAD_FAST 1 (b)
6 BINARY_OP 0 (+)
10 STORE_FAST 2 (c)
4 12 LOAD_FAST 2 (c)
14 RETURN_VALUE
```Đầu ra chính xác thay đổi theo phiên bản Python. Mã byte là chi tiết triển khai CPython, không phải là tập lệnh bên ngoài ổn định.
## 30.5 Độ lệch lệnh
Mỗi lệnh có một vị trí trong luồng mã byte.
Việc tháo gỡ hiển thị điều này như một sự bù đắp:```text id="p6y2o6"
0 RESUME
2 LOAD_FAST
4 LOAD_FAST
6 BINARY_OP
10 STORE_FAST
```Offset được sử dụng bởi:```text id="qv3e7s"
jump instructions
exception tables
line number mapping
tracebacks
debuggers
profilers
coverage tools
```Lệnh nhảy có thể nhắm tới một offset khác.
Về mặt khái niệm:```text id="3ew1cz"
POP_JUMP_IF_FALSE 24
```có nghĩa:```text id="c0kqvz"
if condition is false, continue execution at bytecode offset 24
```Các chi tiết CPython hiện đại khác nhau, nhưng ý tưởng thì giống nhau: mã byte là một chuỗi các hướng dẫn có thể định địa chỉ.
## 30.6 Hiệu ứng ngăn xếp
Mỗi lệnh đều có hiệu ứng ngăn xếp.
Hiệu ứng ngăn xếp mô tả cách lệnh thay đổi ngăn xếp giá trị của khung.
| Hướng dẫn | Xếp chồng trước | Xếp chồng sau |
|---|---|---|
|`LOAD_CONST` | `[]` | `[constant]` |
| `LOAD_FAST` | `[]` | `[local]` |
| `STORE_FAST` | `[value]` | `[]` |
| `BINARY_OP` | `[left, right]` | `[result]` |
| `LOAD_ATTR` | `[object]` | `[attribute]` |
| `CALL` | `[callable, args...]` | `[result]` |
| `RETURN_VALUE` | `[value]`| thoát khỏi khung |
Trình biên dịch phải phát ra mã byte với kỷ luật ngăn xếp hợp lệ. Tại mỗi lệnh, ngăn xếp phải chứa các giá trị mà lệnh mong đợi.
Tại các điểm hợp nhất luồng điều khiển, tất cả các đường dẫn đến phải tạo ra các hình dạng ngăn xếp tương thích.
## 30.7 Hướng dẫn tải cơ bản
Tải hướng dẫn đẩy các giá trị vào ngăn xếp.
Các loại tải phổ biến:
| Hướng dẫn | Ý nghĩa |
|---|---|
|`LOAD_CONST`| Đẩy một hằng số từ`co_consts` |
| `LOAD_FAST`| Đẩy một biến cục bộ từ một khe cục bộ nhanh |
|`LOAD_GLOBAL`| Đẩy tên toàn cầu hoặc tên dựng sẵn |
|`LOAD_DEREF`| Đẩy một giá trị ô đóng |
|`LOAD_ATTR`| Đẩy một thuộc tính từ một đối tượng |
|`LOAD_NAME`| Đẩy tên bằng cách sử dụng lớp hoặc tra cứu không gian tên động |
Ví dụ:```python id="0nyz5p"
def f(x):
return x + 1
```Mã byte khái niệm:```text id="zc9cpj"
LOAD_FAST x
LOAD_CONST 1
BINARY_OP +
RETURN_VALUE
LOAD_FASTđọc một khe từ khung hiện tại.LOAD_CONSTđọc từ đối tượng mã.
30.8 Hướng dẫn về cửa hàng
Hướng dẫn lưu trữ sử dụng các giá trị từ ngăn xếp và đặt chúng ở đâu đó.
| Hướng dẫn | Ý nghĩa |
|---|---|
STORE_FAST |
Lưu trữ vào một khe cục bộ nhanh |
STORE_GLOBAL |
Lưu trữ vào không gian tên chung |
STORE_NAME |
Lưu trữ vào không gian tên cục bộ hiện tại |
STORE_ATTR |
Lưu trữ vào thuộc tính đối tượng |
STORE_SUBSCR |
Lưu trữ vào mục tiêu đăng ký |
STORE_DEREF |
Lưu trữ vào ô đóng cửa |
DELETE_FAST |
Xóa một vị trí cục bộ |
DELETE_ATTR |
Xóa một thuộc tính |
DELETE_SUBSCR |
Xóa một mục |
Ví dụ:python id="dvy1us" def f(a, b): c = a + b return c Hành vi ngăn xếp khái niệm:```text id="env4qv"
LOAD_FAST a stack: [a]
LOAD_FAST b stack: [a, b]
BINARY_OP + stack: [result]
STORE_FAST c stack: []
`STORE_FAST`tiêu thụ kết quả. Nó không để lại giá trị được gán trên ngăn xếp trừ khi trình biên dịch sao chép rõ ràng nó cho mục đích sử dụng khác.
## 30.9 Hướng dẫn biến cục bộ
Hướng dẫn cục bộ nhanh sử dụng chỉ mục, không phải tên.
Vì:```python id="dse6a4"
def f(a, b):
c = a + b
return c
```Trình biên dịch gán các vị trí cục bộ:
| Khe | Tên |
|---:|---|
| 0 |`a`|
| 1 |`b`|
| 2 |`c`|
Hướng dẫn:```text id="wz4dzl"
LOAD_FAST 1
```có nghĩa:```text id="nxyj1j"
push local slot 1, which is b
```Điều này rẻ hơn nhiều so với việc tra cứu từ điển. Khung lưu trữ các cục bộ nhanh theo bố cục giống như mảng.
## 30.10 Hướng dẫn liên tục
Các hằng số được lưu trữ trong`co_consts`.
Ví dụ:```python id="j8ju43"
def f():
return 123
```Đối tượng mã khái niệm:```text id="2kqu9y"
co_consts:
[None, 123]
bytecode:
LOAD_CONST 1
RETURN_VALUE
```Bảng hằng số có thể chứa:```text id="b0tjvm"
None
numbers
strings
bytes
tuples of constants
frozensets of constants
nested code objects
```Các hàm lồng nhau và sự hiểu biết thường xuất hiện dưới dạng các đối tượng mã lồng nhau bên trong`co_consts`.
## 30.11 Hướng dẫn đặt tên
Tra cứu tên phụ thuộc vào phạm vi.
Ở cấp độ mô-đun:```python id="axbo96"
x = 10
print(x)
```tên tồn tại trong từ điển mô-đun.
Bên trong một chức năng:```python id="0nxa3s"
def f():
return x
```nếu như`x`không phải là cục bộ, CPython thực hiện tra cứu toàn cục hoặc tích hợp.
Hướng dẫn quan trọng:
| Hướng dẫn | Sử dụng điển hình |
|---|---|
|`LOAD_GLOBAL`| Chức năng tra cứu toàn cục và tích hợp |
|`LOAD_NAME`| Tra cứu nội dung lớp và không gian tên động |
|`STORE_NAME`| Phân công không gian tên mô-đun hoặc lớp |
|`LOAD_FAST`| Chức năng tra cứu slot cục bộ |
|`LOAD_DEREF`| Tra cứu biến đóng cửa |
Trình biên dịch chọn lệnh dựa trên phân tích bảng ký hiệu.
## 30.12 Hướng dẫn thuộc tính
Truy cập thuộc tính sử dụng các hướng dẫn như`LOAD_ATTR`Và`STORE_ATTR`.
```python id="vqlcss"
value = obj.x
```Về mặt khái niệm:```text id="so9d8z"
LOAD_FAST obj
LOAD_ATTR x
STORE_FAST value
```Tra cứu thuộc tính có thể liên quan đến:```text id="bq7pdp"
object type
descriptor protocol
instance dictionary
slots
class dictionary
base classes
custom __getattribute__
custom __getattr__
inline caches
```Nhưng hiệu ứng ngăn xếp ở cấp mã byte rất đơn giản:```text id="nxg6f5"
LOAD_ATTR:
input: [object]
output: [attribute_value]
```Đối với nhiệm vụ:```python id="5m00ah"
obj.x = value
```Về mặt khái niệm:```text id="y7heq6"
LOAD_FAST value
LOAD_FAST obj
STORE_ATTR x
```Thứ tự toán hạng chính xác được xác định bằng cách triển khai opcode.
## 30.13 Hướng dẫn đăng ký
Đăng ký sử dụng toán hạng ngăn xếp.```python id="0323zx"
value = xs[i]
```Mã byte khái niệm:```text id="kvxa2c"
LOAD_FAST xs
LOAD_FAST i
BINARY_SUBSCR
STORE_FAST value
```các`BINARY_SUBSCR`lệnh sử dụng vùng chứa và khóa, sau đó đẩy kết quả.
Đối với nhiệm vụ:```python id="tnoqbn"
xs[i] = value
```Về mặt khái niệm:```text id="ey4z88"
LOAD_FAST value
LOAD_FAST xs
LOAD_FAST i
STORE_SUBSCR
```Điều này gọi giao thức gán mục của đối tượng.
Để xóa:```python id="edq3j2"
del xs[i]
```trình biên dịch phát ra mã byte đăng ký theo định hướng xóa.
## 30.14 Hoạt động nhị phân
Sử dụng CPython hiện đại`BINARY_OP`cho nhiều phép toán nhị phân, với toán hạng mô tả phép toán cụ thể.
Biểu thức Python:```python id="dgwszy"
a + b
```Mã byte khái niệm:```text id="g1haxi"
LOAD_FAST a
LOAD_FAST b
BINARY_OP +
```Các hoạt động khác bao gồm:```text id="7wzr1z"
+
-
*
@
/
%
//
**
<<
>>
&
|
^
```Các biến thể tại chỗ cũng tồn tại về mặt khái niệm:```python id="zg641m"
x += y
```Điều này có thể sử dụng một biểu mẫu hoạt động tại chỗ hoặc một biến thể toán hạng cố gắng ngữ nghĩa tại chỗ.
Các hoạt động nhị phân rất năng động.`+`có thể có nghĩa là cộng số nguyên, cộng float, nối chuỗi, nối danh sách hoặc do người dùng xác định`__add__`.
## 30.15 Hoạt động đơn nhất
Hoạt động đơn nguyên tiêu thụ một giá trị ngăn xếp và đẩy một kết quả.
Ví dụ:```python id="45q3s0"
-x
+x
~x
not x
```Mã byte khái niệm:```text id="0wm8tb"
LOAD_FAST x
UNARY_NEGATIVE
```Hiệu ứng ngăn xếp:```text id="4h9g0l"
input: [x]
output: [-x]
```Các hoạt động đơn nhất vẫn sử dụng ngữ nghĩa đối tượng Python.`-x`có thể gọi`x.__neg__()`cho các đối tượng do người dùng định nghĩa.
## 30.16 Hướng dẫn so sánh
Việc so sánh sử dụng toán hạng và đưa ra kết quả.```python id="kybtui"
a < b
```Về mặt khái niệm:```text id="lpn8y7"
LOAD_FAST a
LOAD_FAST b
COMPARE_OP <
```Các thao tác so sánh bao gồm:```text id="viop49"
<
<=
==
!=
>
>=
in
not in
is
is not
exception matching
```So sánh có thể gọi mã người dùng:```python id="iwk7b1"
class X:
def __lt__(self, other):
return True
```Vì vậy, ngay cả một lệnh so sánh cũng có thể phân bổ, gọi mã Python, đưa ra một ngoại lệ hoặc trả về các đối tượng không phải Boolean trong một số bối cảnh giao thức trước khi kiểm tra tính xác thực.
## 30.17 Hướng dẫn nhảy
Lệnh nhảy thay đổi con trỏ lệnh.
Họ thực hiện:```text id="td78o9"
if statements
while loops
for loops
boolean short-circuiting
conditional expressions
exception flow
pattern matching branches
```Ví dụ:```python id="gtmgg4"
def f(x):
if x:
return 1
return 0
```Mã byte khái niệm:```text id="gsgii8"
LOAD_FAST x
POP_JUMP_IF_FALSE else_branch
LOAD_CONST 1
RETURN_VALUE
else_branch:
LOAD_CONST 0
RETURN_VALUE
```Một số bước nhảy là vô điều kiện. Một số kiểm tra giá trị ngăn xếp hàng đầu. Một số bảo tồn giá trị. Một số bật nó lên.
Hiệu ứng ngăn xếp của một bước nhảy cũng quan trọng như mục tiêu của nó.
## 30.18 Hướng dẫn vòng lặp
Vòng lặp sử dụng các lệnh nhảy cộng với các lệnh dành riêng cho trình vòng lặp.
MỘT`while`vòng lặp:```python id="1vfdnh"
while cond:
body()
```về mặt khái niệm biên dịch thành:```text id="kbpb8k"
loop_start:
evaluate cond
if false, jump loop_end
execute body
jump loop_start
loop_end:
```MỘT`for`vòng lặp:```python id="5q6rsq"
for x in xs:
body(x)
```sử dụng hướng dẫn giao thức iterator:```text id="x43wuw"
LOAD_FAST xs
GET_ITER
loop:
FOR_ITER end
STORE_FAST x
...
JUMP loop
end:
FOR_ITERgiữ iterator trên ngăn xếp trong khi quá trình lặp tiếp tục.
30.19 Hướng dẫn gọi
Cuộc gọi là một trong những hoạt động mã byte quan trọng nhất về hiệu suất.
Vì:python id="e7dc4b" result = f(a, b) thiết lập ngăn xếp khái niệm:```text id="a7s51h"
LOAD_FAST f
LOAD_FAST a
LOAD_FAST b
CALL 2
STORE_FAST result
Các cuộc gọi có thể nhắm mục tiêu:```text id="t77949"
Python functions
built-in functions
bound methods
classes
callable instances
C extension functions
coroutines
descriptors
```CPython sử dụng các quy ước gọi được tối ưu hóa như vectorcall để giảm việc tạo bộ dữ liệu và từ điển tạm thời.
## 30.20 Hướng dẫn trả lại
Lệnh quay lại thoát khỏi khung hiện tại.```python id="k8kmjw"
def f():
return 42
```Mã byte khái niệm:```text id="tq86ob"
LOAD_CONST 42
RETURN_VALUE
```Ngăn xếp trước khi trở về:```text id="5ks5b0"
[42]
RETURN_VALUEtiêu thụ đối tượng trả về và đưa nó cho người gọi.
Đối với các hàm không có trả về rõ ràng:python id="j1sx5u" def f(): pass trình biên dịch trả vềNone.
Về mặt khái niệm:```text id="x839pp" LOAD_CONST None RETURN_VALUE
Đưa ra một ngoại lệ cũng sử dụng mã byte.```python id="c411i6"
raise ValueError("bad")
```Về mặt khái niệm:```text id="2swrvt"
LOAD_GLOBAL ValueError
LOAD_CONST "bad"
CALL 1
RAISE_VARARGS 1
```Lệnh raise thoát khỏi quá trình thực thi thông thường và chuyển sang giai đoạn lan truyền ngoại lệ.
Nó phải tương tác với:```text id="nkhmtx"
thread exception state
tracebacks
exception handlers
finally blocks
context and cause
frame unwinding
```Lệnh tăng lương thường không mang lại kết quả bình thường.
## 30.22 Hướng dẫn xử lý ngoại lệ
Mã byte xử lý ngoại lệ đã thay đổi đáng kể trên các phiên bản Python.
CPython hiện đại sử dụng các bảng ngoại lệ được liên kết với các đối tượng mã thay vì các mã ngăn xếp khối cũ hơn cho nhiều tác vụ.
Tuy nhiên, trình thông dịch cần hướng dẫn và siêu dữ liệu cho:```text id="3wj1o7"
entering handlers
matching exception types
binding exception variables
reraising
clearing exception state
running finally blocks
handling with-statements
```Ví dụ:```python id="7pkq92"
try:
risky()
except ValueError as exc:
recover(exc)
```Mã được biên dịch phải mô tả:```text id="fxm4bb"
protected bytecode range
handler target
stack depth to restore
exception matching operation
binding of exc
cleanup of exc
```Mã byte ngoại lệ rất nhạy cảm vì nó phải bảo toàn ngữ nghĩa Python trong khi làm sạch các giá trị ngăn xếp tạm thời một cách chính xác.
## 30.23 Hướng dẫn nhập
Quá trình nhập có các hoạt động mã byte chuyên dụng.```python id="gik0n1"
import math
```Về mặt khái niệm:```text id="m9nu5u"
LOAD_CONST level
LOAD_CONST fromlist
IMPORT_NAME math
STORE_NAME math
```Vì:```python id="zoxgo7"
from math import sqrt
```hoạt động khái niệm bao gồm:```text id="n577z3"
IMPORT_NAME math
IMPORT_FROM sqrt
STORE_NAME sqrt
```Nhập mã byte gọi máy nhập. Nó có thể thực thi mã mô-đun, lấy khóa nhập, tải mã byte được lưu trong bộ nhớ đệm, chạy khởi tạo gói hoặc gây ra lỗi nhập.
Câu lệnh nhập là mã thực thi, không phải là khai báo tĩnh.
## 30.24 Hướng dẫn về vùng chứa
Nghĩa đen của vùng chứa sử dụng hướng dẫn xây dựng.```python id="fofmnt"
xs = [a, b, c]
```Về mặt khái niệm:```text id="8ezo7g"
LOAD_FAST a
LOAD_FAST b
LOAD_FAST c
BUILD_LIST 3
STORE_FAST xs
```Các hướng dẫn xây dựng khác bao gồm các thao tác cho:```text id="xge9ra"
tuples
sets
dicts
slices
strings
lists from comprehensions
maps from key-value pairs
```Đối với nghĩa đen trong từ điển:```python id="2pyoy0"
d = {"x": a, "y": b}
```trình biên dịch sắp xếp các khóa và giá trị để lệnh xây dựng chính tả có thể sử dụng chúng.
## 30.25 Hướng dẫn giải nén
Hướng dẫn giải nén phân hủy các giá trị lặp lại.```python id="75qhe0"
a, b = pair
```Về mặt khái niệm:```text id="h2jtqv"
LOAD_FAST pair
UNPACK_SEQUENCE 2
STORE_FAST a
STORE_FAST b
```Giải nén mở rộng:```python id="b1ifoe"
a, *middle, z = values
```sử dụng hướng dẫn giải nén có khả năng tạo danh sách cho mục tiêu được gắn dấu sao.
Hướng dẫn giải nén phải thực thi đúng nguyên tắc. Nếu iterable có quá ít hoặc quá nhiều giá trị, CPython sẽ báo lỗi.
## 30.26 Hướng dẫn đóng
Các hàm lồng nhau yêu cầu các hướng dẫn liên quan đến việc đóng.
Ví dụ:```python id="ji8q8n"
def outer():
x = 10
def inner():
return x
return inner
```Trình biên dịch phải sắp xếp`x`sống trong một vật thể tế bào.
Các hoạt động liên quan bao gồm:```text id="miqfx4"
make cell variables
load closure cells
load dereferenced values
store dereferenced values
build function with closure
```Về mặt khái niệm:```text id="xdtmaw"
outer frame:
x stored in cell
inner function:
closure points to same cell
inner bytecode:
LOAD_DEREF x
```Đây là cách`inner`có thể truy cập`x`sau đó`outer`trở lại.
## 30.27 Hướng dẫn tạo hàm
A`def`câu lệnh tạo một đối tượng hàm khi chạy.```python id="az4vj6"
def f(x):
return x + 1
```Tại thời điểm thực thi mô-đun, CPython không chỉ đăng ký một hàm tĩnh. Nó thực thi mã byte để xây dựng một đối tượng hàm từ một đối tượng mã.
Về mặt khái niệm:```text id="oxvnd7"
LOAD_CONST <code object f>
MAKE_FUNCTION
STORE_NAME f
```Nếu hàm có giá trị mặc định, chú thích, giá trị mặc định của từ khóa hoặc ô đóng, thì chúng sẽ được tải và đính kèm trong quá trình tạo hàm.
Điều này giải thích tại sao`def`có thể thực thi được:```python id="vb8p68"
if debug:
def f():
return "debug"
else:
def f():
return "normal"
```Chỉ có một nhánh tạo và liên kết`f`.
## 30.28 Hướng dẫn tạo lớp
A`class`câu lệnh cũng thực thi mã byte.```python id="4c6kdt"
class C:
x = 1
```Hành vi cấp cao mang tính khái niệm:```text id="n94lef"
load build_class
load class body code object
make function for class body
load class name
call build_class
store class object
```Bản thân thân lớp có một đối tượng mã. Nó chạy trong một không gian tên được chuẩn bị sẵn. Sau khi nó kết thúc, siêu dữ liệu sẽ tạo đối tượng lớp thực tế.
Điều này giải thích tại sao thân lớp có thể chạy mã tùy ý:```python id="x8cd5y"
class C:
print("building class")
x = compute()
```Vòng lặp đánh giá thực thi phần thân đó giống như mã khác.
## 30.29 Hướng dẫn về trình tạo và Coroutine
Trình tạo và coroutine cần có hướng dẫn để tạm dừng và tiếp tục.
Ví dụ:```python id="0q3kic"
def gen():
yield 1
yield 2
```MỘT`yield`lệnh trả về một giá trị cho người gọi trong khi vẫn giữ nguyên trạng thái khung.
Về mặt khái niệm:```text id="47ev86"
LOAD_CONST 1
YIELD_VALUE
resume later
LOAD_CONST 2
YIELD_VALUE
resume later
LOAD_CONST None
RETURN_VALUE
```Coroutine sử dụng các cơ chế liên quan để`await`.
```python id="sbeqhd"
async def f():
result = await g()
return result
```Mã byte phải hỗ trợ:```text id="b2khca"
creating coroutine objects
awaiting awaitables
suspending execution
resuming with values
resuming with exceptions
returning final result
```## 30.30 Hướng dẫn khớp mẫu
So khớp mẫu cấu trúc biên dịch thành các bài kiểm tra chuyên biệt, giải nén, truy cập thuộc tính, kiểm tra ánh xạ, kiểm tra trình tự và các nhánh.
Ví dụ:```python id="1rrlbw"
match value:
case [x, y]:
return x + y
case _:
return 0
```Trình biên dịch phát ra mã byte gần như thực hiện:```text id="6oglmv"
load subject
check sequence pattern
check length
unpack values
bind x and y
execute body
otherwise try next case
```Mã byte khớp mẫu phải bảo toàn ngữ nghĩa Python xung quanh các kết quả khớp không thành công. Các ràng buộc từ các lựa chọn thay thế thất bại không được rò rỉ sai vào các trường hợp thành công sau này.
## 30.31 Hướng dẫn về bộ đệm
CPython hiện đại bao gồm các mục bộ đệm nội tuyến được liên kết với một số hướng dẫn mã byte.
Khi tháo gỡ, bạn có thể thấy các mục liên quan đến bộ đệm tùy thuộc vào tùy chọn và phiên bản.
Các mục bộ đệm này hỗ trợ chuyên môn hóa cho các hoạt động như:```text id="j0ixza"
attribute access
global lookup
binary operations
method calls
function calls
subscript operations
```Các mục trong bộ đệm không phải là hoạt động bình thường của Python. Chúng là siêu dữ liệu thông dịch viên.
Hiệu ứng ngăn xếp của lệnh logic vẫn là phần ngữ nghĩa quan trọng.
Ví dụ:```text id="8sx1vs"
LOAD_ATTR name
CACHE
CACHE
LOAD_ATTRvẫn tiêu thụ một đối tượng và đẩy một giá trị thuộc tính. Các mục bộ đệm giúp thực hiện việc đó nhanh hơn.
30.32 Hướng dẫn thích ứng
Mã byte thích ứng cho phép CPython chuyên môn hóa các hoạt động nóng.
Một thao tác chung có thể được viết lại hoặc diễn giải như một dạng chuyên biệt sau khi thực hiện lặp đi lặp lại.
Ví dụ về luồng khái niệm:text id="08ouu3" BINARY_OP observes int + int repeatedly ↓ specialized int-add path Lệnh chuyên biệt phải giữ nguyên hợp đồng ngăn xếp:```text id="8f1uvg"
input: [left, right]
output: [result]
Cơ chế này mang lại những cải tiến về hiệu suất mà không thay đổi ngữ nghĩa ở cấp độ Python.
## 30.33 Hướng dẫn giả
Một số tên lệnh có thể xuất hiện trong phần bên trong của trình biên dịch hoặc siêu dữ liệu được tạo nhưng không xuất hiện dưới dạng mã hoạt động thời gian chạy thông thường trong luồng mã byte cuối cùng.
Các hướng dẫn giả có thể giúp thể hiện:```text id="5ql4dw"
abstract control-flow operations
exception handling structure
compiler intermediate forms
assembler-level markers
```Khi đọc nội bộ CPython, hãy phân biệt:```text id="ccdgxn"
source-level syntax
compiler intermediate instructions
runtime bytecode instructions
inline cache entries
generated metadata
```Không phải mọi tên trong các tệp liên quan đến opcode đều hoạt động giống như một lệnh thông thường được thực thi bởi vòng đánh giá.
## 30.34 Hướng dẫn Gia đình
Nhiều hướng dẫn thuộc về gia đình.
Ví dụ:```text id="36dyw4"
load family
store family
delete family
binary operation family
unary operation family
jump family
call family
import family
closure family
container-build family
exception family
```Hướng dẫn gia đình giúp bạn đọc tháo gỡ.
Khi bạn nhìn thấy`LOAD_*`, mong đợi một giá trị được đẩy.
Khi bạn nhìn thấy`STORE_*`, mong đợi một giá trị được tiêu thụ.
Khi bạn nhìn thấy`JUMP_*`, mong đợi luồng điều khiển sẽ thay đổi.
Khi bạn nhìn thấy`CALL`, mong đợi bố cục ngăn xếp đối số và có thể gọi được sẽ quan trọng.
## 30.35 Hướng dẫn trung lập về ngăn xếp
Một số hướng dẫn không thay đổi ngăn xếp giá trị logic của Python.
Ví dụ có thể bao gồm:```text id="cos45t"
RESUME
NOP
cache-related entries
some instrumentation markers
```Các hướng dẫn này vẫn có thể quan trọng đối với trạng thái thực thi, truy tìm, chuyên môn hóa hoặc trình thông dịch.
Lệnh trung lập ngăn xếp có thể ảnh hưởng đến hành vi thời gian chạy ngay cả khi nó không đẩy hoặc bật đối tượng Python.
Ví dụ,`RESUME`đánh dấu các điểm tiếp tục thực thi trong mã byte CPython hiện đại.
## 30.36 Sự khác biệt về phiên bản
Mã byte CPython thay đổi trên các phiên bản.
Những thay đổi có thể bao gồm:```text id="kiol94"
new opcodes
removed opcodes
combined opcodes
different call protocol
different exception handling representation
different cache layout
different jump semantics
different disassembly format
specialized instruction changes
```Đây là lý do tại sao mã byte nên được coi là dành riêng cho phiên bản.
Mã phụ thuộc vào mã byte chính xác sẽ khai báo phiên bản Python nào nó nhắm mục tiêu.
Ví dụ về các công cụ phân biệt phiên bản:```text id="yl9axc"
bytecode transformers
coverage tools
debuggers
decompilers
optimizers
security analyzers
teaching visualizers
profilers
```Đối với mã ứng dụng Python thông thường, chi tiết mã byte thường không liên quan. Đối với nội bộ CPython, chúng là trung tâm.
## 30.37 Đọc mã byte bằng tay
Một quá trình đọc hữu ích:```text id="4mbysd"
identify locals
identify constants
identify names
track stack effects
mark jumps
mark call sites
mark exception regions
mark return paths
```Ví dụ:```python id="9opwew"
def f(a, b):
if a > b:
return a - b
return b - a
```Mã byte khái niệm:```text id="c9ffv9"
LOAD_FAST a
LOAD_FAST b
COMPARE_OP >
POP_JUMP_IF_FALSE else
LOAD_FAST a
LOAD_FAST b
BINARY_OP -
RETURN_VALUE
else:
LOAD_FAST b
LOAD_FAST a
BINARY_OP -
RETURN_VALUE
```Theo dõi ngăn xếp:```text id="45f5d2"
LOAD_FAST a [a]
LOAD_FAST b [a, b]
COMPARE_OP > [a > b]
POP_JUMP... []
```Cả hai nhánh đều trả về trực tiếp, do đó không có sự hợp nhất sau nhánh.
## 30.38 Ví dụ: Hiểu danh sách
Nguồn:```python id="k2d22v"
def f(xs):
return [x * 2 for x in xs if x > 0]
```Về mặt khái niệm, CPython tạo một đối tượng mã lồng nhau để dễ hiểu.
Chức năng bên ngoài:```text id="4gr87o"
load comprehension code object
make function
load xs
get iterator
call comprehension function
return result
```Mã hiểu bên trong:```text id="c4xj0s"
build empty list
for each x in iterator:
if x > 0:
append x * 2
return list
```Điều này giải thích tại sao sự hiểu biết có phạm vi riêng của chúng.
Luồng lệnh mã byte làm cho điều này hiển thị vì đối tượng mã lồng nhau xuất hiện trong`co_consts`.
## 30.39 Ví dụ: Đóng cửa
Nguồn:```python id="9gbvx8"
def outer(x):
def inner(y):
return x + y
return inner
```Các khái niệm mã byte quan trọng:```text id="5c33p4"
x becomes a cell variable in outer
x becomes a free variable in inner
outer creates inner with closure data
inner uses LOAD_DEREF to read x
```Điều tra:```python id="x2ckc7"
def outer(x):
def inner(y):
return x + y
return inner
print(outer.__code__.co_cellvars)
inner = outer(10)
print(inner.__code__.co_freevars)
print(inner.__closure__)
```Hướng dẫn mã byte hiển thị cấu trúc đóng và quyền truy cập vô căn cứ.
## 30.40 Ví dụ: Thử Ngoại trừ
Nguồn:```python id="g6xgtw"
def f(x):
try:
return 10 / x
except ZeroDivisionError:
return 0
```Cấu trúc khái niệm:```text id="eylcef"
protected region:
LOAD_CONST 10
LOAD_FAST x
BINARY_OP /
RETURN_VALUE
handler:
check exception matches ZeroDivisionError
LOAD_CONST 0
RETURN_VALUE
```Đối tượng mã chứa siêu dữ liệu bảng ngoại lệ mô tả phạm vi mã byte nào được bảo vệ và nơi bắt đầu trình xử lý.
Khi đọc mã byte này, hãy kiểm tra cả hai:```text id="caeiew"
instruction stream
exception table
```Bảng ngoại lệ là một phần của cấu trúc thực thi.
## 30.41 Mã byte và dòng nguồn
Hướng dẫn mã byte được ánh xạ trở lại vị trí nguồn.
Bản đồ này hỗ trợ:```text id="vn48tw"
tracebacks
debuggers
coverage tools
profilers
line tracing
error messages
```Một dòng nguồn duy nhất có thể biên dịch thành nhiều hướng dẫn mã byte.```python id="8bi6zl"
x = f(a) + g(b)
```Mã byte khái niệm bao gồm:```text id="n0ncx0"
load f
load a
call f
load g
load b
call g
binary add
store x
```Siêu dữ liệu vị trí nguồn cho phép CPython báo cáo vị trí chính xác hơn về lỗi và theo dõi sự kiện.
## 30,42 Mã byte và truy nguyên
Khi một ngoại lệ xảy ra, quá trình truy nguyên sẽ ghi lại khung và vị trí nguồn/lệnh liên quan.
Ví dụ:```python id="lzbl8z"
def f(x):
return 10 / x
f(0)
```Hoạt động thất bại là mã byte chia. CPython sử dụng đối tượng mã của khung và vị trí lệnh để báo cáo dòng nguồn.
Do đó, truy nguyên được kết nối với:```text id="cc3baw"
frame
code object
instruction offset
source location table
exception state
```## 30.43 Mã byte và tối ưu hóa
CPython thực hiện một số tối ưu hóa thời gian biên dịch và thời gian chạy.
Các ví dụ về thời gian biên dịch có thể bao gồm:```text id="3ej829"
constant handling
dead code handling in simple cases
jump simplification
stack size computation
scope resolution
literal container optimizations
```Ví dụ về thời gian chạy bao gồm:```text id="ta6h3c"
adaptive specialization
inline caches
optimized call paths
fast locals
specialized attribute access
specialized global lookup
```Luồng lệnh bytecode nằm giữa trình biên dịch và trình tối ưu hóa thời gian chạy. Nó vừa là đầu ra của trình biên dịch vừa là đầu vào của trình thông dịch.
## 30.44 Mã byte không phải là API ổn định
Mã byte CPython không được thiết kế làm mục tiêu máy ảo công cộng ổn định.
Nó có thể thay đổi giữa các bản phát hành để hỗ trợ:```text id="18txrq"
better performance
simpler interpreter implementation
new language features
better debugging information
new exception machinery
new call conventions
specialization
free-threading work
JIT experiments
```Điều này không có nghĩa là mã byte không thể sử dụng được. Điều đó có nghĩa là các công cụ cấp mã byte phải nhận biết được phiên bản.
Để chương trình hoạt động ổn định, hãy dựa vào ngữ nghĩa của ngôn ngữ Python. Đối với công việc nội bộ CPython, hãy nghiên cứu mã byte cho phiên bản CPython chính xác.
## 30.45 Trình thông dịch mã byte tối thiểu
Một thông dịch viên đồ chơi giúp thể hiện ý tưởng.```python id="bo0wgp"
LOAD_CONST = "LOAD_CONST"
LOAD_FAST = "LOAD_FAST"
STORE_FAST = "STORE_FAST"
ADD = "ADD"
RETURN = "RETURN"
def run(code, consts, locals_):
stack = []
for op, arg in code:
if op == LOAD_CONST:
stack.append(consts[arg])
elif op == LOAD_FAST:
stack.append(locals_[arg])
elif op == STORE_FAST:
locals_[arg] = stack.pop()
elif op == ADD:
right = stack.pop()
left = stack.pop()
stack.append(left + right)
elif op == RETURN:
return stack.pop()
raise RuntimeError("missing RETURN")
```Một chương trình nhỏ:```python id="5gvw2y"
code = [
(LOAD_FAST, "a"),
(LOAD_FAST, "b"),
(ADD, None),
(STORE_FAST, "c"),
(LOAD_FAST, "c"),
(RETURN, None),
]
print(run(code, [], {"a": 2, "b": 3}))
```Đầu ra:```text id="n2zzxm"
5
```Đồ chơi này loại bỏ hầu hết CPython:```text id="mrtfdz"
objects
reference counts
exceptions
calls
descriptors
classes
imports
closures
generators
coroutines
specialization
inline caches
tracing
thread state
```Nhưng nó nắm bắt được ý tưởng cốt lõi: các lệnh mã byte hoạt động trên trạng thái khung và ngăn xếp giá trị.
## 30.46 Những hiểu lầm phổ biến
| Hiểu lầm | Đúng mẫu |
|---|---|
| Bytecode là nguồn Python theo cú pháp khác | Bytecode là luồng hướng dẫn thông dịch |
| Bytecode có thể di chuyển được trên tất cả các triển khai Python | Mã byte CPython dành riêng cho CPython |
| Bytecode ổn định trên các phiên bản | Thay đổi mã byte giữa các phiên bản CPython |
| Hướng dẫn chứa tên biến đầy đủ | Nhiều lệnh chứa các chỉ mục vào các bảng đối tượng mã |
|`dis`đầu ra là câu chuyện thời gian chạy đầy đủ | Thời gian chạy cũng sử dụng khung, bộ đệm, bảng ngoại lệ và chuyên môn hóa |
| Một dòng nguồn có nghĩa là một hướng dẫn | Một dòng thường biên dịch thành nhiều hướng dẫn |
| Bytecode luôn ánh xạ trực tiếp tới cú pháp | Một số mã byte tồn tại cho máy móc giao thức thời gian chạy |
| Bộ nhớ đệm nội tuyến là các hoạt động của Python | Chúng là siêu dữ liệu tối ưu hóa trình thông dịch |
## 30.47 Chiến lược đọc
Để hiểu các hướng dẫn mã byte, hãy làm việc từ các ví dụ nhỏ.
Bắt đầu với:```python id="6w8kmn"
def f(a, b):
return a + b
```Sau đó kiểm tra:```python id="0tvkkr"
import dis
dis.dis(f)
```Sau đó kiểm tra:```python id="6h63o9"
print(f.__code__.co_consts)
print(f.__code__.co_varnames)
print(f.__code__.co_names)
print(f.__code__.co_stacksize)
```Đối với mỗi hướng dẫn, hãy hỏi:```text id="w1yd94"
What does it consume from the stack?
What does it push?
Which code object table does it reference?
Can it jump?
Can it raise?
Can it call Python code?
Can specialization change its fast path?
```Phương pháp này mở rộng từ số học đơn giản đến các hàm, bao đóng, nhập, lớp, ngoại lệ và hiểu.
## 30.48 Tóm tắt chương
Hướng dẫn mã byte là định dạng hướng dẫn thực thi của CPython. Chúng sống bên trong các đối tượng mã và được thực thi bởi các khung thông qua vòng đánh giá.
Mô hình cốt lõi là:```text id="rbfch8"
code object holds bytecode and metadata
frame holds execution state
bytecode instruction mutates frame state
evaluation loop dispatches instructions
stack effects define operand flow
```Hướng dẫn tải giá trị, lưu trữ giá trị, gọi hàm, thực hiện các thao tác, xây dựng vùng chứa, nhánh, xử lý các ngoại lệ, tạo hàm và lớp, nhập mô-đun, tạm dừng trình tạo và trả về kết quả.
Bytecode nhỏ gọn, năng động, dựa trên ngăn xếp và dành riêng cho phiên bản. Việc hiểu nó sẽ mang lại cho bạn cái nhìn trực tiếp về cách nguồn Python trở thành thực thi CPython.