Skip to content

Commit a349e3d

Browse files
committed
ch07
1 parent 61b87cb commit a349e3d

1 file changed

Lines changed: 151 additions & 0 deletions

File tree

ch07.md

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
# Python 的编译结果——Code 对象与 pyc 文件
2+
3+
## Python 脚本的执行过程
4+
5+
- 编译
6+
7+
Python 编译器将 py 文件编译成字节码(PyCodeObject)。
8+
9+
- 执行
10+
11+
Python 虚拟机执行字节码。
12+
13+
14+
## PyCodeObject
15+
16+
PyCodeObject 定义:
17+
18+
```C
19+
// Include/code.h
20+
21+
typedef struct {
22+
PyObject_HEAD
23+
int co_argcount; /* #arguments, except *args */
24+
int co_nlocals; /* #local variables */
25+
int co_stacksize; /* #entries needed for evaluation stack */
26+
int co_flags; /* CO_..., see below */
27+
PyObject *co_code; /* instruction opcodes */
28+
PyObject *co_consts; /* list (constants used) */
29+
PyObject *co_names; /* list of strings (names used) */
30+
PyObject *co_varnames; /* tuple of strings (local variable names) */
31+
PyObject *co_freevars; /* tuple of strings (free variable names) */
32+
PyObject *co_cellvars; /* tuple of strings (cell variable names) */
33+
/* The rest doesn't count for hash/cmp */
34+
PyObject *co_filename; /* string (where it was loaded from) */
35+
PyObject *co_name; /* string (name, for reference) */
36+
int co_firstlineno; /* first source line number */
37+
PyObject *co_lnotab; /* string (encoding addr<->lineno mapping) */
38+
void *co_zombieframe; /* for optimization only (see frameobject.c) */
39+
} PyCodeObject;
40+
```
41+
42+
Python 会对源代码中的每个代码块(Code Block)生成一个 PyCodeObject 对象,一个作用域(名字空间)就算做一个代码块。
43+
44+
代码块对应的字节码就存放在 PyCodeObject 的 `co_code`字段。
45+
46+
## 生成 pyc 文件
47+
48+
pyc文件包含三部分信息:
49+
50+
- magic number
51+
52+
用于标识 pyc 版本,不同Python 版本的 magic number 不一样。通过 magic number 可以避免 Python 加载错误版本的 pyc 文件。
53+
54+
Python2.5 的 magic number 为:
55+
56+
```C
57+
#define MAGIC (62131 | ((long)'\r'<<16) | ((long)'\n'<<24))
58+
59+
/* Magic word as global; note that _PyImport_Init() can change the
60+
value of this global to accommodate for alterations of how the
61+
compiler works which are enabled by command line switches. */
62+
static long pyc_magic = MAGIC;
63+
```
64+
65+
- mtime
66+
67+
文件修改时间,用于和 py 文件的修改时间进行对比。Python 在导入模块时优先导入pyc 文件,但是有可能 pyc 文件对应的 py 文件已经被修改了,所以 Python 需要对比 pyc 文件和 py 文件的修改时间,如果 py 文件的修改时间更新,则需要重新编译 pyc 文件。
68+
69+
- 字节码,即 PyCodeObject。
70+
71+
生成 pyc 文件的代码如下:
72+
73+
```C
74+
// Python/import.c
75+
76+
/* Write a compiled module to a file, placing the time of last
77+
modification of its source into the header.
78+
Errors are ignored, if a write error occurs an attempt is made to
79+
remove the file. */
80+
81+
static void
82+
write_compiled_module(PyCodeObject *co, char *cpathname, time_t mtime)
83+
{
84+
FILE *fp;
85+
86+
fp = open_exclusive(cpathname);
87+
if (fp == NULL) {
88+
if (Py_VerboseFlag)
89+
PySys_WriteStderr(
90+
"# can't create %s\n", cpathname);
91+
return;
92+
}
93+
PyMarshal_WriteLongToFile(pyc_magic, fp, Py_MARSHAL_VERSION);
94+
/* First write a 0 for mtime */
95+
PyMarshal_WriteLongToFile(0L, fp, Py_MARSHAL_VERSION);
96+
PyMarshal_WriteObjectToFile((PyObject *)co, fp, Py_MARSHAL_VERSION);
97+
if (fflush(fp) != 0 || ferror(fp)) {
98+
if (Py_VerboseFlag)
99+
PySys_WriteStderr("# can't write %s\n", cpathname);
100+
/* Don't keep partial file */
101+
fclose(fp);
102+
(void) unlink(cpathname);
103+
return;
104+
}
105+
/* Now write the true mtime */
106+
fseek(fp, 4L, 0);
107+
assert(mtime < LONG_MAX);
108+
PyMarshal_WriteLongToFile((long)mtime, fp, Py_MARSHAL_VERSION);
109+
fflush(fp);
110+
fclose(fp);
111+
if (Py_VerboseFlag)
112+
PySys_WriteStderr("# wrote %s\n", cpathname);
113+
}
114+
```
115+
116+
`PyMarshal_WriteObjectToFile`最终调用 `w_object`将 PyCodeObject 写入文件中,`w_object`的代码如下(有删减):
117+
118+
```C
119+
// Python/marchal.c
120+
121+
// w_object 的代码很长,都是 if/else if 判断 object 的具体类型。
122+
123+
static void
124+
w_object(PyObject *v, WFILE *p)
125+
{
126+
...
127+
else if (PyCode_Check(v)) {
128+
PyCodeObject *co = (PyCodeObject *)v;
129+
w_byte(TYPE_CODE, p);
130+
w_long(co->co_argcount, p);
131+
w_long(co->co_nlocals, p);
132+
w_long(co->co_stacksize, p);
133+
w_long(co->co_flags, p);
134+
w_object(co->co_code, p);
135+
w_object(co->co_consts, p);
136+
w_object(co->co_names, p);
137+
w_object(co->co_varnames, p);
138+
w_object(co->co_freevars, p);
139+
w_object(co->co_cellvars, p);
140+
w_object(co->co_filename, p);
141+
w_object(co->co_name, p);
142+
w_long(co->co_firstlineno, p);
143+
w_object(co->co_lnotab, p);
144+
}
145+
...
146+
```
147+
148+
## dis
149+
150+
Python 标准库中的 dis 模块可以用来获取代码对应的字节码。
151+

0 commit comments

Comments
 (0)