23. Đối tượng mã
23. Đối tượng mã
Đối tượng mã là bản trình bày được biên dịch của CPython về mã Python thực thi.
Nó chứa mã byte và siêu dữ liệu. Trình thông dịch có thể thực thi nó, nhưng bản thân đối tượng mã không mang trạng thái thời gian chạy như toàn cục, đối số mặc định, ô đóng hoặc phương thức bị ràng buộc.
Đối với nguồn này:python def add(a, b): return a + b đối tượng chức năngaddchứa một đối tượng mã:```python
code = add.code
print(code.co_name) print(code.co_varnames) print(code.co_consts) print(code.co_names)
## 23.1 Vị trí trong Đường dẫn biên dịch
Các đối tượng mã là đầu ra của quá trình biên dịch.```text
source text
↓
tokenization
↓
parsing
↓
AST
↓
symbol table
↓
compiler
↓
code object
↓
frame execution
```Đối tượng mã là sự chuyển giao giữa trình biên dịch và trình thông dịch.
Trình biên dịch phát ra các đối tượng mã.
Vòng đánh giá thực thi các đối tượng mã bên trong các khung.
## 23.2 Đối tượng mã và Đối tượng hàm
Một đối tượng hàm bao bọc một đối tượng mã.
Ví dụ:```python
def f(x):
return x + 1
```Khi chạy:```text
function object
__code__ code object
__globals__ module globals dictionary
__defaults__ positional defaults
__kwdefaults__ keyword-only defaults
__closure__ closure cells
__dict__ function attributes
__name__ function name
__qualname__ qualified name
```Đối tượng mã chứa nội dung được biên dịch.```text
code object
bytecode
constants
names
local variable names
free variable names
cell variable names
filename
line information
stack size
flags
```Về mặt lý thuyết, cùng một đối tượng mã có thể được sử dụng bởi nhiều đối tượng hàm.```python
import types
def f(x):
return x + 1
g = types.FunctionType(f.__code__, globals(), "g")
print(g(10))
```Mã được chia sẻ. Trình bao bọc hàm thay đổi liên kết thời gian chạy.
## 23.3 Đối tượng mã là bất biến
Các đối tượng mã là bất biến.
Sau khi được tạo, mã byte, hằng số, tên biến, cờ và siêu dữ liệu của đối tượng mã không thể thay đổi tại chỗ.
Điều này quan trọng đối với thiết kế an toàn và thời gian chạy. Nhiều hàm hoặc khung có thể tham chiếu đến cùng một đối tượng mã. Nếu các đối tượng mã có thể thay đổi được thì việc thay đổi một đối tượng có thể ảnh hưởng đến mã hiện đang chạy.
Để sửa đổi mã, các công cụ tạo một đối tượng mã mới.
Python hiện đại bộc lộ một`replace()`phương pháp:```python
def f():
return 1
new_code = f.__code__.replace(co_name="renamed")
```Điều này tạo ra một bản sao được sửa đổi thay vì chỉnh sửa đối tượng ban đầu.
## 23.4 Đối tượng mã mô-đun
Một mô-đun cũng có một đối tượng mã.
Ví dụ:```python
src = """
x = 1
y = 2
print(x + y)
"""
code = compile(src, "example.py", "exec")
```Đối tượng mã này đại diện cho việc thực thi cấp cao nhất.
Nó không có tham số chức năng. Không gian tên cục bộ của nó thường là từ điển mô-đun.
Thanh tra:```python
print(code.co_name)
print(code.co_filename)
print(code.co_consts)
print(code.co_names)
print(code.co_varnames)
```Đối với mã mô-đun,`co_name`thường xuyên`"<module>"`.
Trình thông dịch thực thi đối tượng mã này với toàn cục và cục bộ. Nhập thông thường, thực thi tập lệnh và`exec()`tất cả đều sử dụng các đối tượng mã kiểu mô-đun.
## 23.5 Đối tượng mã biểu thức`compile()`cũng có thể tạo ra các đối tượng mã biểu thức.
Ví dụ:```python
code = compile("1 + 2", "<input>", "eval")
result = eval(code)
print(result)
```MỘT`eval`đối tượng mã đại diện cho một biểu thức. Nó trả về giá trị biểu thức.
Điều này khác với`exec`cách thức:```python
compile("x = 1", "<input>", "exec")
compile("1 + 2", "<input>", "eval")
execchế độ chấp nhận các câu lệnh.evalchế độ chỉ chấp nhận một biểu thức.
Chế độ này thay đổi cả điểm bắt đầu ngữ pháp và hành vi của đối tượng mã được tạo.
23.6 Đối tượng mã tương tác
Sử dụng chế độ tương tác"single"biên soạn.
Ví dụ:```python
code = compile("1 + 2", "
Đây là lý do tại sao REPL hoạt động khác với việc thực thi tập lệnh.```text
exec mode:
execute statements normally
eval mode:
return expression value
single mode:
behave like interactive input
```Đối tượng mã ghi lại đủ thông tin để trình thông dịch chạy chính xác chế độ đã chọn.
## 23.7 Đối tượng mã lồng nhau
Các cấu trúc thực thi lồng nhau tạo ra các đối tượng mã lồng nhau.
Ví dụ:```python
def outer(x):
def inner(y):
return x + y
return inner
```Đối tượng mã mô-đun chứa đối tượng mã cho`outer`TRONG`co_consts`.
các`outer`đối tượng mã chứa đối tượng mã cho`inner`riêng của nó`co_consts`.
Thanh tra:```python
def outer(x):
def inner(y):
return x + y
return inner
outer_code = outer.__code__
print(outer_code.co_consts)
for const in outer_code.co_consts:
if isinstance(const, type(outer_code)):
print("nested code:", const.co_name)
```Các đối tượng mã lồng nhau là các hằng số vì chúng là dữ liệu được biên dịch được nhúng trong đối tượng mã kèm theo.
Vào thời gian chạy,`MAKE_FUNCTION`tạo các đối tượng hàm từ các đối tượng mã đó.
## 23,8`co_code`
`co_code`lưu trữ byte mã byte.
Ví dụ:```python
def f(a, b):
return a + b
print(f.__code__.co_code)
```thô`co_code`đọc trực tiếp không dễ chịu chút nào. Sử dụng`dis`:
```python
import dis
dis.dis(f)
```Bytecode là luồng lệnh cho máy ảo CPython.
Mỗi lệnh có một opcode và có thể có một đối số. Mã hóa chính xác thay đổi giữa các phiên bản, vì vậy các công cụ nên sử dụng`dis`thay vì bù đắp byte mã hóa cứng.
## 23,9`co_consts`
`co_consts`lưu trữ các hằng số được tham chiếu bởi mã byte.
Ví dụ:```python
def f():
return 1, "x", None
```Thanh tra:```python
print(f.__code__.co_consts)
```Nội dung điển hình:```text
(None, 1, 'x')
```Các hằng số có thể bao gồm:```text
None
booleans
numbers
strings
bytes
tuples of constants
frozensets
nested code objects
```Trình biên dịch loại bỏ trùng lặp một số hằng số trong một đối tượng mã.
Hướng dẫn mã byte tải hằng số theo chỉ mục:```text
LOAD_CONST 1
```có nghĩa là “tải`co_consts[1]`.”
## 23.10`co_names`
`co_names`lưu trữ tên được sử dụng để tra cứu toàn cầu, tra cứu thuộc tính, nhập và các hoạt động tương tự.
Ví dụ:```python
def f(xs):
return len(xs)
```Thanh tra:```python
print(f.__code__.co_names)
```Có khả năng đầu ra:```text
('len',)
```Mã byte sử dụng một chỉ mục vào`co_names`:
```text
LOAD_GLOBAL 0
```có nghĩa là “tải tên chung hoặc tên dựng sẵn`co_names[0]`.”
Ví dụ với quyền truy cập thuộc tính:```python
def f(obj):
return obj.value
valuexuất hiện ởco_namesvì tên thuộc tính cũng được lưu trữ ở đó.
23.11co_varnames
co_varnameslưu trữ tên biến cục bộ.
Ví dụ:python def f(a, b): c = a + b return c Thanh tra:python print(f.__code__.co_varnames) Đầu ra điển hình:text ('a', 'b', 'c') Mã byte cục bộ nhanh sử dụng các chỉ mục trong bộ dữ liệu này.```text
LOAD_FAST 0 a
LOAD_FAST 1 b
STORE_FAST 2 c
## 23.12`co_freevars`Và`co_cellvars`Việc đóng cửa sử dụng hai trường đối tượng mã.
| Lĩnh vực | Ý nghĩa |
| ------------- | ---------------------------------------- |
|`co_cellvars`| Người dân địa phương bị bắt bởi phạm vi lồng nhau |
|`co_freevars`| Các biến được ghi lại từ phạm vi kèm theo |
Ví dụ:```python
def outer():
x = 1
def inner():
return x
return inner
```Thanh tra:```python
print(outer.__code__.co_cellvars)
inner = outer()
print(inner.__code__.co_freevars)
```Hình dạng dự kiến:```text
('x',)
('x',)
```Vì`outer`, `x`là một biến ô vì mã lồng nhau cần nó.
Vì`inner`, `x`là một biến miễn phí vì nó xuất phát từ một phạm vi kèm theo.
## 23.13 Ô đóng cửa không được lưu trữ trong đối tượng mã
Một đối tượng mã ghi lại những biến rảnh rỗi mà nó cần. Nó không lưu trữ các giá trị thực tế được ghi lại.
Các giá trị được ghi lại nằm trong các ô đóng được gắn vào đối tượng hàm.
Ví dụ:```python
def make_reader(value):
def read():
return value
return read
f = make_reader(42)
print(f.__code__.co_freevars)
print(f.__closure__)
print(f.__closure__[0].cell_contents)
```Đối tượng mã nói:```text
this function needs a free variable named value
```Đối tượng chức năng cung cấp:```text
the actual cell containing 42
```Sự tách biệt này cho phép tái sử dụng cùng một đối tượng mã lồng nhau với các giá trị được ghi lại khác nhau.
## 23.14`co_argcount`và siêu dữ liệu đối số
Đối tượng mã lưu trữ số lượng đối số.
Các trường quan trọng bao gồm:```text
co_argcount
co_posonlyargcount
co_kwonlyargcount
co_varnames
co_flags
```Ví dụ:```python
def f(a, b, /, c, *, d):
return a, b, c, d
```Thanh tra:```python
code = f.__code__
print(code.co_argcount)
print(code.co_posonlyargcount)
print(code.co_kwonlyargcount)
print(code.co_varnames)
```Các trường này giúp máy gọi hàm liên kết các đối số với các vị trí biến cục bộ.
Giá trị mặc định không được lưu trữ trong đối tượng mã. Chúng sống trên đối tượng hàm:```python
def f(x=1):
return x
print(f.__defaults__)
print(f.__code__.co_consts)
```Giá trị mặc định thuộc về đối tượng hàm vì giá trị mặc định được đánh giá tại thời điểm định nghĩa hàm.
## 23,15`co_flags`
`co_flags`lưu trữ các cờ thực thi.
Cờ mô tả các thuộc tính như:```text
has *args
has **kwargs
is generator
is coroutine
is async generator
uses nested scopes
future flags
```Ví dụ:```python
def normal():
return 1
def gen():
yield 1
async def coro():
return 1
```Thanh tra:```python
print(normal.__code__.co_flags)
print(gen.__code__.co_flags)
print(coro.__code__.co_flags)
```Bộ thực thi sử dụng các cờ này để quyết định đối tượng nào sẽ được tạo khi hàm được gọi.
Hàm tạo trả về một đối tượng trình tạo.
Hàm coroutine trả về một đối tượng coroutine.
Một hàm bình thường sẽ thực thi bình thường và trả về một giá trị.
## 23.16`co_stacksize`
`co_stacksize`ghi lại độ sâu ngăn xếp đánh giá tối đa mà đối tượng mã cần.
Ví dụ:```python
def f(a, b, c):
return a + b * c
```Trình biên dịch tính toán mức sử dụng ngăn xếp từ các hiệu ứng ngăn xếp mã byte.
Về mặt khái niệm:```text
LOAD_FAST a stack depth 1
LOAD_FAST b stack depth 2
LOAD_FAST c stack depth 3
BINARY_OP * stack depth 2
BINARY_OP + stack depth 1
RETURN_VALUE stack depth 0
```Độ sâu tối đa: 3.
Khung sử dụng`co_stacksize`để phân bổ đủ dung lượng lưu trữ ngăn xếp.
## 23.17 Siêu dữ liệu nguồn
Đối tượng mã lưu trữ siêu dữ liệu nguồn.
Các trường quan trọng bao gồm:```text
co_filename
co_name
co_qualname
co_firstlineno
line table data
position table data
```Ví dụ:```python
def f():
x = 1
return x
code = f.__code__
print(code.co_filename)
print(code.co_name)
print(code.co_qualname)
print(code.co_firstlineno)
```Siêu dữ liệu này hỗ trợ:```text
tracebacks
debuggers
profilers
coverage tools
inspection
warnings
error locations
```Nếu không có siêu dữ liệu nguồn, việc thực thi Python vẫn có thể thực hiện được nhưng việc chẩn đoán sẽ tệ hơn nhiều.
## 23.18 Bảng và vị trí đường
Các đối tượng mã ánh xạ độ lệch mã byte trở lại vị trí nguồn.
Bạn có thể kiểm tra các vị trí:```python
def f(x):
return x + 1
for item in f.__code__.co_positions():
print(item)
```Ánh xạ nguồn này hỗ trợ truy nguyên và gỡ lỗi chính xác.
Các phiên bản CPython cũ hơn sử dụng các định dạng bảng số dòng khác nhau. Các phiên bản hiện đại hiển thị thông tin vị trí phong phú hơn, bao gồm cả độ lệch cột.
Các công cụ nên ưu tiên các phương thức công khai hơn là phân tích cú pháp trực tiếp các bảng nhị phân riêng tư.
## 23.19 Bảng ngoại lệ
Các đối tượng mã CPython hiện đại bao gồm thông tin bảng ngoại lệ.
Các bảng ngoại lệ mô tả phạm vi mã byte nào được bảo vệ bởi trình xử lý ngoại lệ.
Ví dụ:```python
def f():
try:
risky()
except ValueError:
recover()
```Bản thân mã byte không đủ. Trình thông dịch cũng cần siêu dữ liệu như:```text
protected instruction range
handler target
stack depth restoration information
handler kind
```Siêu dữ liệu này cho phép CPython triển khai`try`, `except`, `finally`và luồng điều khiển liên quan.
## 23.20 Tạo đối tượng mã từ`compile()`các`compile()`tích hợp tạo ra các đối tượng mã.
Ví dụ:```python
code = compile("x = 1\n", "<input>", "exec")
ns = {}
exec(code, ns)
print(ns["x"])
```Đối với biểu thức:```python
code = compile("1 + 2", "<input>", "eval")
print(eval(code))
```Đối với đầu vào AST:```python
import ast
tree = ast.parse("x = 1\n")
code = compile(tree, "<ast>", "exec")
compile()chạy cùng một đường dẫn rộng được sử dụng cho các tệp:```text
source or AST
↓
validation
↓
symbol analysis
↓
bytecode generation
↓
code object
Các đối tượng mã có thể được thực thi với`exec()`hoặc`eval()`.
Ví dụ:```python
code = compile("x = 10\n", "<input>", "exec")
globals_dict = {}
locals_dict = {}
exec(code, globals_dict, locals_dict)
print(locals_dict["x"])
exec()cung cấp không gian tên thời gian chạy.
Bản thân đối tượng mã không chứa các không gian tên đó.
Đối với mã biểu thức:```python code = compile("x + 1", "", "eval")
print(eval(code, {"x": 41}))
## 23.22 Đối tượng và khung mã
Khung là một phiên bản đang chạy của một đối tượng mã.
Đối tượng mã:```text
immutable compiled instructions
```Khung:```text
current execution state
instruction pointer
local variables
evaluation stack
block state
globals
builtins
exception state
```Ví dụ:```python
def f(x):
y = x + 1
return y
```Mỗi cuộc gọi đến`f`tạo hoặc sử dụng khung thực thi`f.__code__`.
Nhiều cuộc gọi sử dụng cùng một đối tượng mã nhưng trạng thái khung khác nhau.```text
f.__code__
shared by all calls
frame for f(1)
x = 1
y = 2
frame for f(10)
x = 10
y = 11
```## 23.23 Đối tượng mã và`marshal`CPython có thể tuần tự hóa các đối tượng mã bằng`marshal`.
biên soạn`.pyc`các tệp chứa các đối tượng mã được sắp xếp theo thứ tự cộng với siêu dữ liệu tiêu đề.
Đây là cách thực hiện cụ thể. các`marshal`định dạng không phải là định dạng tuần tự hóa có mục đích chung ổn định.
Quy tắc thực tế:```text
use pickle or another format for application data
use marshal only for CPython internals or closely related tooling
```Khi Python nhập một mô-đun, CPython có thể tải một mô-đun được lưu vào bộ nhớ đệm`.pyc`tệp, đọc đối tượng mã và thực thi nó thay vì biên dịch lại nguồn.
## 23.24 Đối tượng mã trong`.pyc`Tập tin
A`.pyc`tập tin lưu trữ dữ liệu bộ đệm bytecode đã biên dịch.
Về mặt khái niệm:```text
.pyc file
header
magic number
invalidation metadata
marshalled module code object
```Đối tượng mã mô-đun có thể chứa các đối tượng mã nội dung lớp và hàm lồng nhau trong`co_consts`.
Bộ đệm mã byte tăng tốc độ khởi động bằng cách tránh phân tích cú pháp và biên dịch lặp đi lặp lại khi nguồn không thay đổi.
Nhưng`.pyc`các tập tin có phiên bản cụ thể. Các định dạng bytecode và marshal có thể thay đổi trên các phiên bản CPython.
## 23.25 Ranh giới bảo mật đối tượng mã
Đối tượng mã là dữ liệu thực thi.
Chạy một đối tượng mã tương đương với việc chạy mã.
Ví dụ:```python
code = compile("import os; os.remove('file.txt')", "<input>", "exec")
exec(code)
```Đối tượng mã không trở nên an toàn vì nó đã được biên dịch.
Các hệ thống nhạy cảm về bảo mật không được thực thi các đối tượng mã không đáng tin cậy, nguồn không đáng tin cậy hoặc mã byte được sắp xếp theo thứ tự không đáng tin cậy.
Quá trình biên dịch không phải là sandboxing.
## 23.26 Xem xét nội tâm đối tượng mã
Các đối tượng mã rất hữu ích cho việc xem xét nội tâm.
Ví dụ:```python
def f(a, b=1):
c = a + b
return c
code = f.__code__
for name in [
"co_argcount",
"co_posonlyargcount",
"co_kwonlyargcount",
"co_nlocals",
"co_stacksize",
"co_flags",
"co_consts",
"co_names",
"co_varnames",
"co_freevars",
"co_cellvars",
]:
print(name, getattr(code, name))
```Điều này hỗ trợ các công cụ như:```text
debuggers
profilers
coverage tools
tracers
decorators
test frameworks
bytecode inspectors
static analysis helpers
```## 23.27 Thay thế đối tượng mã
Các đối tượng mã có thể được sao chép với các sửa đổi bằng cách sử dụng`replace()`.
Ví dụ:```python
def f():
return 1
new_code = f.__code__.replace(co_name="g")
```Điều này rất hữu ích cho các công cụ nâng cao.
Nhưng việc thay đổi nội bộ đối tượng mã có thể dễ dàng vi phạm các giả định. Ví dụ: mã byte, hằng số, tên, kích thước ngăn xếp, cờ và bảng ngoại lệ phải nhất quán.
Đối tượng mã bị hỏng có thể làm hỏng công cụ, gây ra lỗi khó hiểu hoặc hoạt động không đúng.
Sử dụng`replace()`để chỉnh sửa siêu dữ liệu hoặc công việc mã byte được xác thực cẩn thận.
## 23.28 Đối tượng mã được dành riêng cho từng phiên bản
Các trường đối tượng mã và chi tiết mã byte thay đổi trên các phiên bản Python.
Ví dụ về các khu vực nhạy cảm với phiên bản:```text
opcode names
opcode arguments
inline cache layout
exception table format
line table format
code object constructor signature
optimization behavior
```Các công cụ mạnh mẽ nên sử dụng API công khai:```text
dis
inspect
types.CodeType.replace
co_positions()
co_lines()
ast
compile
```Tránh giả định rằng bố cục mã byte thô vẫn ổn định.
## 23,29 Vùng nguồn CPython
Các tệp nguồn CPython quan trọng bao gồm:```text
Include/cpython/code.h
Objects/codeobject.c
Python/compile.c
Python/assemble.c
Python/flowgraph.c
Python/marshal.c
Lib/dis.py
Lib/inspect.py
Lib/types.py
```Vai trò khái niệm:
| Khu vực | Vai trò |
| -------------- | ---------------------------------- |
|`code.h`| Định nghĩa cấu trúc đối tượng mã |
|`codeobject.c`| Tạo và hành vi đối tượng mã |
|`compile.c`| AST để tạo lệnh |
|`assemble.c`| Tập hợp đối tượng mã cuối cùng |
|`flowgraph.c`| Xử lý biểu đồ luồng điều khiển |
|`marshal.c`| Tuần tự hóa cho bộ đệm mã byte |
|`dis.py`| Kiểm tra mã byte có thể đọc được của con người |
## 23.30 Mô hình tinh thần tối thiểu
Sử dụng mô hình này:```text
A code object is immutable compiled Python code.
It contains bytecode plus metadata.
A function object wraps a code object with runtime context.
A frame executes a code object.
Nested functions, lambdas, classes, comprehensions, and generators have nested code objects.
Constants, names, locals, free variables, and cell variables are stored in code object tables.
The interpreter uses the code object to allocate frames and run bytecode.
```Đối tượng mã là tạo phẩm được biên dịch để kết nối trình biên dịch của CPython với máy ảo của nó.