24. Hằng số, tên và địa phương
24. Hằng, Tên và Địa phương
Đối tượng mã không lưu trữ mã nguồn Python dưới dạng văn bản. Nó lưu trữ các bảng nhỏ gọn và các hướng dẫn mã byte tham chiếu đến các bảng đó theo chỉ mục.
Ba trong số các bảng quan trọng nhất là:```text id="mx2a9w" co_consts co_names co_varnames
Đối với chức năng này:```python id="wg80pj"
def f(a):
b = len(a)
return b + 1
```Cửa hàng CPython:```text id="jxh1ou"
constants:
None
1
names:
len
local variables:
a
b
```Sau đó, các hướng dẫn mã byte sẽ tham chiếu đến các mục đó theo chỉ mục.
## 24.1 Vị trí trong Đường dẫn biên dịch
Các hằng số, tên và địa phương được tập hợp trong quá trình biên dịch.```text id="la84x8"
AST
↓
symbol table
↓
compiler
↓
instruction stream
↓
constant table
↓
name table
↓
local variable table
↓
code object
```Các bảng này là một phần của đối tượng mã.
Trình thông dịch sử dụng chúng khi thực thi mã byte.```text id="gvqxes"
LOAD_CONST 1
LOAD_GLOBAL 0
LOAD_FAST 0
STORE_FAST 1
```Mỗi toán hạng số lập chỉ mục vào một trong các bảng của đối tượng mã.
## 24.2 Tại sao đối tượng mã lại sử dụng bảng
Mã byte cần phải nhỏ gọn.
Thay vì nhúng trực tiếp các đối tượng hoặc chuỗi Python đầy đủ vào bên trong mỗi lệnh, CPython lưu trữ chúng một lần trong một bảng.
Ví dụ:```python id="y6ymck"
def f():
print("hello")
print("hello")
```Chuỗi`"hello"`có thể xuất hiện một lần trong`co_consts`.
Tên`"print"`có thể xuất hiện một lần trong`co_names`.
Mã byte tham chiếu chúng theo chỉ mục.
Về mặt khái niệm:```text id="y1yh5e"
co_consts:
0: None
1: "hello"
co_names:
0: "print"
bytecode:
LOAD_GLOBAL 0 print
LOAD_CONST 1 "hello"
CALL
POP_TOP
LOAD_GLOBAL 0 print
LOAD_CONST 1 "hello"
CALL
POP_TOP
```Điều này làm giảm sự trùng lặp và mang lại cho trình thông dịch vị trí tra cứu ổn định.
## 24.3 Hằng số
Các hằng số là các giá trị bằng chữ và các tạo phẩm được biên dịch được lưu trữ trong`co_consts`.
Các hằng số phổ biến bao gồm:```text id="0sqmi6"
None
True
False
integers
floats
complex numbers
strings
bytes
tuples of constants
frozensets of constants
nested code objects
```Ví dụ:```python id="0bigls"
def f():
return 123, "abc", None
```Thanh tra:```python id="unotfl"
print(f.__code__.co_consts)
```Hình dạng điển hình:```text id="zsqktw"
(None, 123, 'abc')
```Bộ dữ liệu được hàm trả về có thể tự nó được xây dựng từ các hằng số hoặc nó có thể được lưu trữ dưới dạng một bộ dữ liệu không đổi nếu trình biên dịch có thể gấp nó một cách an toàn.
## 24.4`None`Hằng số
Hầu hết các đối tượng mã bao gồm`None`TRONG`co_consts`.
Ví dụ:```python id="mxsc3g"
def f():
x = 1
```Mặc dù nguồn không ghi rõ ràng`return None`, hàm trả về`None`ngầm.
Trình biên dịch phải có khả năng phát ra:```text id="b7i1tf"
LOAD_CONST None
RETURN_VALUE
```Ở cấp độ mô-đun, CPython cũng thường phát ra kết quả cuối cùng`RETURN_VALUE`với`None`để kết thúc việc thực hiện.
## 24.5 Hằng số
Chữ số thường xuất hiện trong`co_consts`.
Ví dụ:```python id="y88hr2"
def f():
return 10 + 20
```AST có thể chứa`10 + 20`, nhưng trình biên dịch có thể gấp cái này thành`30`.
Thanh tra:```python id="y7a6vi"
import dis
def f():
return 10 + 20
print(f.__code__.co_consts)
dis.dis(f)
```Bạn có thể chỉ thấy`30`như một hằng số được tải.
Gấp liên tục là bảo thủ. CPython có thể gấp các biểu thức an toàn và mang tính quyết định tại thời điểm biên dịch. Nó không thể gấp các biểu thức với các hiệu ứng thời gian chạy.
Ví dụ:```python id="3nempa"
def f():
return 10 + x
xkhông được biết tại thời điểm biên dịch, vì vậy điều này không thể trở thành một hằng số.
24.6 Hằng số chuỗi và byte
Chuỗi ký tự và byte xuất hiện trongco_consts.
Ví dụ:python id="086py8" def f(): return "hello", b"world" Thanh tra:python id="mwit9a" print(f.__code__.co_consts) Các chuỗi ký tự liền kề có thể được nối trong quá trình biên dịch:```python id="yn2nfn"
def f():
return "hello" "world"
Kết quả là một hằng số:```text id="g8bxm7"
"helloworld"
```## 24.7 Hằng số bộ dữ liệu
Các bộ chỉ chứa hằng số có thể được lưu trữ dưới dạng hằng số.
Ví dụ:```python id="u33byh"
def f():
return (1, 2, 3)
```Trình biên dịch có thể lưu trữ toàn bộ bộ dữ liệu:```text id="kw4dlg"
(1, 2, 3)
```TRONG`co_consts`.
Nhưng nếu một phần tử là động:```python id="f0psq9"
def f(x):
return (1, x, 3)
```bộ dữ liệu phải được xây dựng trong thời gian chạy.
Mẫu biên dịch:```text id="0f7svq"
LOAD_CONST 1
LOAD_FAST x
LOAD_CONST 3
BUILD_TUPLE 3
```Trình biên dịch phân biệt các vùng chứa hằng số bất biến với các vùng chứa được xây dựng trong thời gian chạy.
## 24.8 Hằng số tập hợp đông lạnh
Các hằng số của tập hợp có thể thay đổi được, vì vậy bản thân một tập hợp không thể là hằng số trong mã byte.
Nhưng CPython có thể sử dụng`frozenset`hằng số để kiểm tra thành viên được tối ưu hóa.
Ví dụ:```python id="a4bjga"
def is_small(x):
return x in {1, 2, 3}
```Nguồn sử dụng một chữ cố định. Trình biên dịch có thể biên dịch tư cách thành viên dựa vào một`frozenset`không đổi vì tập hợp này chỉ được sử dụng để kiểm tra tư cách thành viên.
Về mặt khái niệm:```text id="y0dtbp"
LOAD_FAST x
LOAD_CONST frozenset({1, 2, 3})
CONTAINS_OP
```Điều này tránh việc xây dựng lại tập hợp mỗi khi hàm chạy.
Trình biên dịch phải bảo toàn ngữ nghĩa. Sự tối ưu hóa này hợp lệ cho các hằng số vì tư cách thành viên trong`{1, 2, 3}`Và`frozenset({1, 2, 3})`đưa ra kết quả tương tự cho hoạt động có liên quan.
## 24.9 Các đối tượng mã lồng nhau dưới dạng hằng số
Các hàm lồng nhau lưu trữ các đối tượng mã của chúng trong`co_consts`.
Ví dụ:```python id="n17lr5"
def outer():
def inner():
return 1
return inner
```Thanh tra:```python id="56jqwc"
for const in outer.__code__.co_consts:
print(repr(const))
```Bạn sẽ tìm thấy một đối tượng mã cho`inner`.
Trong thời gian chạy, hàm bên ngoài thực thi mã byte tải đối tượng mã lồng nhau và tạo đối tượng hàm:```text id="xrarxf"
LOAD_CONST <code object inner>
MAKE_FUNCTION
STORE_FAST inner
```Đối tượng mã lồng nhau là dữ liệu được biên dịch không đổi. Đối tượng hàm được tạo trong thời gian chạy.
## 24.10 Bảng tên`co_names`lưu trữ các tên tượng trưng được sử dụng bởi các hoạt động mã byte không phải là biến cục bộ hoặc biến đóng nhanh.
Chúng bao gồm:```text id="b9fx1t"
global names
builtin lookup names
attribute names
imported module names
method names
some class body names
```Ví dụ:```python id="sl10xc"
def f(xs):
return len(xs)
```Thanh tra:```python id="il1b6j"
print(f.__code__.co_names)
```Hình dạng điển hình:```text id="6whwgp"
('len',)
```Mã byte tải`len`thông qua một chỉ mục tên.
## 24.11 Toàn cầu và Nội dung
Một cái tên trong`co_names`có thể đề cập đến toàn cầu hoặc nội dung trong thời gian chạy.
Ví dụ:```python id="pxxc09"
def f(xs):
return len(xs)
lenkhông phải là địa phương. Nó được tải bằng cách sử dụng các quy tắc tra cứu toàn cầu.
Kiểm tra tra cứu thời gian chạy:text id="d2z1ce" function globals then builtins Đối tượng mã không lưu trữ hàm dựng sẵn thực tếlen. Nó lưu trữ chuỗi tên"len".
Điều đó có nghĩa là việc thay đổi toàn cầu có thể ảnh hưởng đến việc thực thi:```python id="oghwkx" def f(xs): return len(xs)
len = lambda x: 999
print(f([1, 2, 3]))
```Bên trong mô-đun đó,lenbây giờ giải quyết thành lambda toàn cầu, không phải nội trang.
24.12 Tên thuộc tính
Tên thuộc tính cũng tồn tại trongco_names.
Ví dụ:python id="trjd66" def f(obj): return obj.value Thanh tra:python id="vrg5um" print(f.__code__.co_names) Hình dạng điển hình:text id="133tbx" ('value',) Mã byte sử dụng:text id="f1yae8" LOAD_FAST obj LOAD_ATTR value Chuỗi"value"được lưu trữ một lần trongco_names.
Điều này tách biệt với tra cứu toàn cầu. Cùng một bảng lưu trữ các chuỗi tên được sử dụng bởi nhiều họ lệnh.
24.13 Tên phương thức
Cú pháp gọi phương thức cũng sử dụng tên.
Ví dụ:```python id="pwnbfj" def f(obj): return obj.run()
`run`xuất hiện ở`co_names`.
Trình biên dịch có thể đưa ra các hướng dẫn hướng cuộc gọi chuyên dụng tùy thuộc vào phiên bản Python, nhưng tên phương thức vẫn xuất phát từ bảng tên.
Mã byte khái niệm:```text id="vjlnib"
LOAD_FAST obj
LOAD_METHOD run
CALL 0
```Bảng tên lưu trữ chuỗi tên phương thức.
## 24.14 Nhập tên
Nhập khẩu cũng sử dụng bảng tên.
Ví dụ:```python id="sf2lb3"
def f():
import os
return os.getcwd()
```Bảng tên có thể chứa:```text id="pi43tc"
os
getcwd
```Bảng cục bộ chứa`os`nếu quá trình nhập nằm trong hàm vì quá trình nhập liên kết với một biến cục bộ.
Sự khác biệt này quan trọng:```text id="hc8fxf"
co_names:
names needed by import and attribute operations
co_varnames:
local binding created by import os
```Nhập khẩu là các bài tập từ góc độ bố cục biến cục bộ.
## 24.15 Biến cục bộ`co_varnames`lưu trữ tên biến cục bộ nhanh.
Ví dụ:```python id="nyh7qv"
def f(a, b):
c = a + b
return c
```Thanh tra:```python id="g0h75x"
print(f.__code__.co_varnames)
```Hình dạng điển hình:```text id="ja0vd0"
('a', 'b', 'c')
```Khung lưu trữ các giá trị cục bộ theo bố cục giống như mảng. Lập chỉ mục mã byte vào bố cục này.```text id="x503qw"
LOAD_FAST 0 a
LOAD_FAST 1 b
STORE_FAST 2 c
```Đây là lý do tại sao truy cập cục bộ nhanh hơn tra cứu từ điển toàn cầu.
## 24.16 Tham số cục bộ
Các tham số chức năng xuất hiện đầu tiên trong`co_varnames`.
Ví dụ:```python id="9b48mv"
def f(a, b, c=0):
d = a + b + c
return d
co_varnamesthường bắt đầu:```text id="83pbbd"
('a', 'b', 'c', 'd')
Giá trị mặc định được lưu trữ trên đối tượng hàm, không trực tiếp trong đối tượng mã.```python id="9q7w3m"
print(f.__defaults__)
print(f.__code__.co_varnames)
```## 24.17 Người dân địa phương chỉ có vị trí và từ khóa
Đối tượng mã lưu trữ số lượng đối số riêng biệt với`co_varnames`.
Ví dụ:```python id="87asnp"
def f(a, b, /, c, *, d):
return a, b, c, d
```Thanh tra:```python id="kbjcbq"
code = f.__code__
print(code.co_posonlyargcount)
print(code.co_argcount)
print(code.co_kwonlyargcount)
print(code.co_varnames)
```Tên địa phương được lưu trữ trong`co_varnames`, trong khi số đếm xác định cách diễn giải các mục hàng đầu.
Về mặt khái niệm:```text id="h79uky"
co_varnames:
a, b, c, d
co_posonlyargcount:
2
co_argcount:
3
co_kwonlyargcount:
1
```Máy gọi sử dụng siêu dữ liệu này để liên kết đối số.
## 24.18`*args`Và`**kwargs`Tên đối số biến cũng xuất hiện trong`co_varnames`.
Ví dụ:```python id="a78joh"
def f(a, *args, **kwargs):
return args, kwargs
```Thanh tra:```python id="57zit6"
code = f.__code__
print(code.co_varnames)
print(code.co_flags)
```các`co_flags`trường ghi lại rằng hàm chấp nhận các đối số từ khóa hoặc vị trí thay đổi.
Những cái tên`args`Và`kwargs`là các khe địa phương.
## 24.19 Giá trị tạm thời không được đặt tên cục bộ
Ngăn xếp đánh giá giữ các giá trị tạm thời.
Ví dụ:```python id="yqtwg0"
def f(a, b, c):
return a + b * c
co_varnameschứa:text id="gvos5r" a b c Nó không chứa các giá trị trung gian tạm thời nhưb * c.
Kết quả trung gian đó nằm trong ngăn xếp đánh giá của khung.
Ngăn xếp khái niệm:```text id="3snx1b" LOAD_FAST a stack: a LOAD_FAST b stack: a, b LOAD_FAST c stack: a, b, c BINARY_OP * stack: a, temp BINARY_OP + stack: result RETURN_VALUE stack empty
## 24.20 Biến ô`co_cellvars`lưu trữ các địa phương được nắm bắt bởi các hàm lồng nhau.
Ví dụ:```python id="qbiuv6"
def outer():
x = 1
def inner():
return x
return inner
```Thanh tra:```python id="91ti0b"
print(outer.__code__.co_cellvars)
```Kết quả điển hình:```text id="n2byuv"
('x',)
```Bên trong`outer`, `x`phải sống trong tế bào vì`inner`có thể truy cập nó sau`outer`trở lại.
Biến cục bộ trở thành bộ lưu trữ đóng được hỗ trợ bằng đống.
## 24.21 Biến miễn phí`co_freevars`lưu trữ tên được chụp từ phạm vi kèm theo.
Sử dụng cùng một ví dụ:```python id="b3corm"
inner = outer()
print(inner.__code__.co_freevars)
```Kết quả điển hình:```text id="i1hd7z"
('x',)
```Bên trong`inner`, `x`không phải là một người dân địa phương bình thường. Nó được tải từ một ô đóng cửa.
Mã byte khái niệm:```text id="o8g8eg"
LOAD_DEREF x
RETURN_VALUE
```Đối tượng mã ghi lại tên. Đối tượng hàm mang ô thực tế.
## 24.22 Từ điển địa phương
Bên trong một chức năng được tối ưu hóa thông thường, người dân địa phương