Fail pyc ialah fail bytecode yang dijana apabila Python mentafsir dan melaksanakan kod sumber Ia mengandungi hasil kompilasi kod sumber. dan maklumat metadata yang berkaitan supaya Python boleh memuatkan dan melaksanakan kod dengan lebih cepat.
Berbeza daripada bahasa yang disusun, Python ialah bahasa yang ditafsirkan dan tidak secara langsung menyusun kod sumber ke dalam kod mesin dan melaksanakannya. Sebelum menjalankan kod, jurubahasa Python akan menyusun kod sumber terlebih dahulu ke dalam kod bait, dan kemudian mentafsir kod bait untuk pelaksanaan. Fail .pyc ialah fail bytecode yang dijana semasa proses ini.
Apabila jurubahasa Python melaksanakan fail .py buat kali pertama, ia akan menjana fail .pyc yang sepadan dalam direktori yang sama supaya ia boleh dilaksanakan dengan lebih pantas pada kali berikutnya fail tersebut dimuatkan. Apabila fail sumber diubah suai dan dimuat semula, jurubahasa menjana semula fail .pyc untuk mengemas kini kod bait cache.
Fail python biasa perlu ditukar kepada bytecode melalui pengkompil, dan kemudian bytecode diberikan kepada mesin maya python, dan kemudian mesin maya python melaksanakan bytecode. Proses keseluruhan adalah seperti berikut:
Kami boleh terus menggunakan kompil semua modul untuk menjana fail pyc bagi fail yang sepadan. Perintah
➜ pvm ls demo.py hello.py ➜ pvm python -m compileall . Listing '.'... Listing './.idea'... Listing './.idea/inspectionProfiles'... Compiling './demo.py'... Compiling './hello.py'... ➜ pvm ls __pycache__ demo.py hello.py ➜ pvm ls __pycache__ demo.cpython-310.pyc hello.cpython-310.pyc
python -m compileall .
akan mengimbas fail py secara rekursif dalam direktori semasa dan menjana fail pyc bagi fail yang sepadan.
Bahagian Pertama Nombor ajaib terdiri daripada dua bahagian:
Bahagian Satu Keajaiban terdiri daripada integer 2-bait dan dua aksara lain, carriage return dan suapan baris juga menduduki dua bait, menjadikan jumlah keseluruhan empat bait. Integer dua bait ini berbeza dalam versi python yang berbeza Contohnya, dalam python3.5, nilai ini ialah 3351, dsb., dan dalam python3.9, nilai ini ialah 3420, 3421, 3422, 3423, 3424, dsb. ( dalam versi kecil Python 3.9).
Bahagian 2 Medan Bit Tujuan utama medan ini adalah untuk membolehkan hasil kompilasi yang boleh dihasilkan pada masa hadapan, tetapi dalam python3.9a2, nilai medan ini masih 0. Sila rujuk PEP552 - Deterministic pyc untuk butiran. Medan ini tidak wujud dalam versi awal python2 dan python3 (python3.5 belum tersedia lagi).
Bahagian ketiga ialah saiz keseluruhan fail sumber py.
Bahagian keempat juga merupakan bahagian paling penting dari keseluruhan fail pyc Bahagian terakhir ialah data selepas bersiri objek CodeObject Kami akan menganalisis data yang berkaitan dengan objek ini kemudian.
Sekarang mari kita menganalisis fail pyc secara terperinci Kod python yang sepadan ialah:
def f(): x = 1 return 2
Bentuk heksadesimal bagi fail pyc adalah seperti berikut:
rreeeKerana penggunaan data. Perwakilan endian kecil, jadi untuk data di atas:
Bahagian pertama nombor ajaib ialah: 0xa0d0d6f.
Bahagian kedua Medan Bit ialah: 0x0.
Tarikh pengubahsuaian terakhir bagi bahagian ketiga ialah: 0x642148b9.
Saiz fail bahagian keempat ialah: 0x20 bait, yang bermaksud saiz fail hello.py ialah 32 bait.
Berikut ialah coretan kod kecil untuk membaca maklumat meta pengepala fail pyc:
➜ __pycache__ hexdump -C hello.cpython-310.pyc 00000000 6f 0d 0d 0a 00 00 00 00 b9 48 21 64 20 00 00 00 |o........H!d ...| 00000010 e3 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000020 00 02 00 00 00 40 00 00 00 73 0c 00 00 00 64 00 |.....@...s....d.| 00000030 64 01 84 00 5a 00 64 02 53 00 29 03 63 00 00 00 |d...Z.d.S.).c...| 00000040 00 00 00 00 00 00 00 00 00 01 00 00 00 01 00 00 |................| 00000050 00 43 00 00 00 73 08 00 00 00 64 01 7d 00 64 02 |.C...s....d.}.d.| 00000060 53 00 29 03 4e e9 01 00 00 00 e9 02 00 00 00 a9 |S.).N...........| 00000070 00 29 01 da 01 78 72 03 00 00 00 72 03 00 00 00 |.)...xr....r....| 00000080 fa 0a 2e 2f 68 65 6c 6c 6f 2e 70 79 da 01 66 01 |.../hello.py..f.| 00000090 00 00 00 73 04 00 00 00 04 01 04 01 72 06 00 00 |...s........r...| 000000a0 00 4e 29 01 72 06 00 00 00 72 03 00 00 00 72 03 |.N).r....r....r.| 000000b0 00 00 00 72 03 00 00 00 72 05 00 00 00 da 08 3c |...r....r......<| 000000c0 6d 6f 64 75 6c 65 3e 01 00 00 00 73 02 00 00 00 |module>....s....| 000000d0 0c 00 |..| 000000d2
Keluaran kod di atas adalah seperti berikut:
medan bit = b'00000000'
sihir 0xa0d0d6f
moddate (Isnin 27 Mac 15:41:45 2023)
Saiz Fail 32
<🎜 Untuk operasi fail terperinci, sila lihat kod sumber fail importlib/_bootstrap_external.py perpustakaan standard python.
Dalam CPython, CodeObject
ialah objek, yang mengandungi kod bait, pemalar, pembolehubah, parameter kedudukan, parameter kata kunci dan maklumat lain kod Python, serta beberapa pengguna Metadata untuk menjalankan kod, seperti nama fail, nombor baris kod, dsb.
Dalam CPython, apabila kita melaksanakan modul atau fungsi Python, jurubahasa akan mula-mula menyusun kodnya ke dalam CodeObject
dan kemudian melaksanakannya. Semasa penyusunan, jurubahasa menukar kod Python kepada kod bait dan menyimpannya dalam objek CodeObject
. Selepas itu, setiap kali kami memanggil modul atau fungsi itu, jurubahasa menggunakan kod bait dalam CodeObject
untuk melaksanakan kod tersebut.
CodeObject
Objek tidak boleh diubah dan tidak boleh diubah suai setelah dibuat. Ini kerana kod bait kod Python tidak boleh diubah, dan objek CodeObject
mengandungi kod bait ini, jadi ia juga tidak boleh diubah.
Dalam artikel ini, kami memperkenalkan kandungan utama objek kod dan secara ringkas memperkenalkan fungsinya Dalam artikel seterusnya, kami akan menganalisis kod sumber yang sepadan dengan objek kod dan fungsi terperinci medan yang sepadan. .
现在举一个例子来分析一下 pycdemo.py 的 pyc 文件,pycdemo.py 的源程序如下所示:
if __name__ == '__main__': a = 100 print(a)
下面的代码是一个用于加载 pycdemo01.cpython-39.pyc 文件(也就是 hello.py 对应的 pyc 文件)的代码,使用 marshal 读取 pyc 文件里面的 code object 。
import marshal import dis import struct import time import types import binascii def print_metadata(fp): magic = struct.unpack('<l', fp.read(4))[0] print(f"magic number = {hex(magic)}") bit_field = struct.unpack('<l', fp.read(4))[0] print(f"bit filed = {bit_field}") t = struct.unpack('<l', fp.read(4))[0] print(f"time = {time.asctime(time.localtime(t))}") file_size = struct.unpack('<l', fp.read(4))[0] print(f"file size = {file_size}") def show_code(code, indent=''): print ("%scode" % indent) indent += ' ' print ("%sargcount %d" % (indent, code.co_argcount)) print ("%snlocals %d" % (indent, code.co_nlocals)) print ("%sstacksize %d" % (indent, code.co_stacksize)) print ("%sflags %04x" % (indent, code.co_flags)) show_hex("code", code.co_code, indent=indent) dis.disassemble(code) print ("%sconsts" % indent) for const in code.co_consts: if type(const) == types.CodeType: show_code(const, indent+' ') else: print(" %s%r" % (indent, const)) print("%snames %r" % (indent, code.co_names)) print("%svarnames %r" % (indent, code.co_varnames)) print("%sfreevars %r" % (indent, code.co_freevars)) print("%scellvars %r" % (indent, code.co_cellvars)) print("%sfilename %r" % (indent, code.co_filename)) print("%sname %r" % (indent, code.co_name)) print("%sfirstlineno %d" % (indent, code.co_firstlineno)) show_hex("lnotab", code.co_lnotab, indent=indent) def show_hex(label, h, indent): h = binascii.hexlify(h) if len(h) < 60: print("%s%s %s" % (indent, label, h)) else: print("%s%s" % (indent, label)) for i in range(0, len(h), 60): print("%s %s" % (indent, h[i:i+60])) if __name__ == '__main__': filename = "./__pycache__/pycdemo01.cpython-39.pyc" with open(filename, "rb") as fp: print_metadata(fp) code_object = marshal.load(fp) show_code(code_object)
执行上面的程序输出结果如下所示:
magic number = 0xa0d0d61 bit filed = 0 time = Tue Mar 28 02:40:20 2023 file size = 54 code argcount 0 nlocals 0 stacksize 2 flags 0040 code b'650064006b02721464015a01650265018301010064025300' 3 0 LOAD_NAME 0 (__name__) 2 LOAD_CONST 0 ('__main__') 4 COMPARE_OP 2 (==) 6 POP_JUMP_IF_FALSE 20 4 8 LOAD_CONST 1 (100) 10 STORE_NAME 1 (a) 5 12 LOAD_NAME 2 (print) 14 LOAD_NAME 1 (a) 16 CALL_FUNCTION 1 18 POP_TOP >> 20 LOAD_CONST 2 (None) 22 RETURN_VALUE consts '__main__' 100 None names ('__name__', 'a', 'print') varnames () freevars () cellvars () filename './pycdemo01.py' name '<module>' firstlineno 3 lnotab b'08010401'
下面是 code object 当中各个字段的作用:
首先需要了解一下代码块这个概念,所谓代码块就是一个小的 python 代码,被当做一个小的单元整体执行。在 Python 中常见的代码块包括函数体、类的定义和模块。
argcount,这个表示一个代码块的参数个数,这个参数只对函数体代码块有用,因为函数可能会有参数,比如上面的 pycdemo.py 是一个模块而不是一个函数,因此这个参数对应的值为 0 。
co_code,这个对象的具体内容就是一个字节序列,存储真实的 python 字节码,主要是用于 python 虚拟机执行的,在本篇文章当中暂时不详细分析。
co_consts,这个字段是一个列表类型的字段,主要是包含一些字符串常量和数值常量,比如上面的 ";main" 和 100 。
co_filename,这个字段的含义就是对应的源文件的文件名。
co_firstlineno,这个字段的含义为在 python 源文件当中第一行代码出现的行数,这个字段在进行调试的时候非常重要。
主要含义是标识该 code object 的类型的字段是 co_flags。0x0080 表示这个 block 是一个协程,0x0010 表示这个 code object 是嵌套的等等。
co_lnotab,这个字段的含义主要是用于计算每个字节码指令对应的源代码行数。
The main purpose of the field "co_varnames" is to indicate a name defined locally in a code object.。
co_names,和 co_varnames 相反,表示非本地定义但是在 code object 当中使用的名字。
co_nlocals,这个字段表示在一个 code object 当中本地使用的变量个数。
co_stackszie,因为 python 虚拟机是一个栈式计算机,这个参数的值表示这个栈需要的最大的值。
co_cellvars,co_freevars,这两个字段主要和嵌套函数和函数闭包有关。
Atas ialah kandungan terperinci Apakah struktur fail pyc mesin maya python?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!