欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

python虛擬機(jī)pyc文件結(jié)構(gòu)的深入理解

 更新時間:2023年03月29日 08:59:44   作者:一無是處的研究僧  
這篇文章主要為大家介紹了python虛擬機(jī)之pyc文件結(jié)構(gòu)的深入探究理解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

深入理解 PYTHON 虛擬機(jī):PYC 文件結(jié)構(gòu)

在本篇文章當(dāng)中主要給大家介紹一下 .py 文件在被編譯之后對應(yīng)的 pyc 文件結(jié)構(gòu),pyc 文件當(dāng)中的一個核心內(nèi)容就是 python 字節(jié)碼。

PYC 文件

pyc 文件是 Python 在解釋執(zhí)行源代碼時生成的一種字節(jié)碼文件,它包含了源代碼的編譯結(jié)果和相關(guān)的元數(shù)據(jù)信息,以便于 Python 可以更快地加載和執(zhí)行代碼。

Python 是一種解釋型語言,它不像編譯型語言那樣將源代碼直接編譯成機(jī)器碼執(zhí)行。Python 的解釋器會在運(yùn)行代碼之前先將源代碼編譯成字節(jié)碼,然后將字節(jié)碼解釋執(zhí)行。.pyc 文件就是這個過程中生成的字節(jié)碼文件。

當(dāng) Python 解釋器首次執(zhí)行一個 .py 文件時,它會在同一目錄下生成一個對應(yīng)的 .pyc 文件,以便于下次加載該文件時可以更快地執(zhí)行。如果源文件在修改之后被重新加載,解釋器會重新生成 .pyc 文件以更新緩存的字節(jié)碼。

生成 PYC 文件

正常的 python 文件需要通過編譯器變成字節(jié)碼,然后將字節(jié)碼交給 python 虛擬機(jī),然后 python 虛擬機(jī)會執(zhí)行字節(jié)碼。整體流程如下所示:

我們可以直接使用 compile all 模塊生成對應(yīng)文件的 pyc 文件。

?  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 . 命令將遞歸掃描當(dāng)前目錄下面的 py 文件,并且生成對應(yīng)文件的 pyc 文件。

PYC 文件布局

第一部分魔數(shù)由兩部分組成:

第一部分 魔術(shù)是由一個 2 字節(jié)的整數(shù)和另外兩個字符回車換行組成的, "\r\n" 也占用兩個字節(jié),一共是四個字節(jié)。這個兩個字節(jié)的整數(shù)在不同的 python 版本還不一樣,比如說在 python3.5 當(dāng)中這個值為 3351 等值,在 python3.9 當(dāng)中這個值為 3420,3421,3422,3423,3424等值(在 python 3.9 的小版本)。

第二部分 Bit Field 這個字段的主要作用是為了將來能夠?qū)崿F(xiàn)復(fù)現(xiàn)編譯結(jié)果,但是在 python3.9a2 時,這個字段的值還全部是 0 。詳細(xì)內(nèi)容可以參考 PEP552-Deterministic pycs 。這個字段在 python2 和 python3 早期版本并沒有(python3.5 還沒有),在 python3 的后期版本這個字段才出現(xiàn)的。

第三部分 就是整個 py 源文件的大小了。

第四部分 也是整個 pyc 文件當(dāng)中最重要的一個部分,最后一個部分就是一個 CodeObject 對象序列化之后的數(shù)據(jù),我們稍后再來仔細(xì)分析一下這個對象相關(guān)的數(shù)據(jù)。

我們現(xiàn)在來具體分析一個 pyc 文件,對應(yīng)的 python 代碼為:

def f():
    x = 1
    return 2

pyc 文件的十六進(jìn)制形式如下所示:

?  __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

因?yàn)閿?shù)據(jù)使用小端表示方式,因此對于上面的數(shù)據(jù)來說:

  • 第一部分魔數(shù)為:0xa0d0d6f 。
  • 第二部分 Bit Field 為:0x0 。
  • 第三部分最后一次修改日期為:0x642148b9 。
  • 第四部分文件大小為:0x20 字節(jié),也就是說 hello.py 這個文件的大小是 32 字節(jié)。

下面是一個小的代碼片段用于讀取 pyc 文件的頭部元信息:

import struct
import time
import binascii
fname = "./__pycache__/hello.cpython-310.pyc"
f = open(fname, "rb")
magic = struct.unpack('<l', f.read(4))[0]
bit_filed = f.read(4)
print(f"bit field = {binascii.hexlify(bit_filed)}")
moddate = f.read(4)
filesz = f.read(4)
modtime = time.asctime(time.localtime(struct.unpack('<l', moddate)[0]))
filesz = struct.unpack('<L', filesz)
print("magic %s" % (hex(magic)))
print("moddate (%s)" % (modtime))
print("File Size %d" % filesz)
f.close()

上面的代碼輸出結(jié)果如下所示:

bit field = b'00000000'
magic 0xa0d0d6f
moddate (Mon Mar 27 15:41:45 2023)
File Size 32

有關(guān) pyc 文件的詳細(xì)操作可以查看 python 標(biāo)準(zhǔn)庫 importlib/_bootstrap_external.py 文件源代碼。

CODEOBJECT

在 CPython 中,CodeObject 是一個對象,它包含了 Python 代碼的字節(jié)碼、常量、變量、位置參數(shù)、關(guān)鍵字參數(shù)等信息,以及一些用于運(yùn)行代碼的元數(shù)據(jù),如文件名、代碼行號等。

在 CPython 中,當(dāng)我們執(zhí)行一個 Python 模塊或函數(shù)時,解釋器會先將其代碼編譯為 CodeObject,然后再執(zhí)行。在編譯過程中,解釋器會將 Python 代碼轉(zhuǎn)換為字節(jié)碼,并將其保存在 CodeObject 對象中。此后,每當(dāng)我們調(diào)用該模塊或函數(shù)時,解釋器都會使用 CodeObject 中的字節(jié)碼來執(zhí)行代碼。

CodeObject 對象是不可變的,一旦創(chuàng)建就不能被修改。這是因?yàn)?Python 代碼的字節(jié)碼是不可變的,而 CodeObject 對象包含了這些字節(jié)碼,所以也是不可變的。

在本篇文章當(dāng)中主要介紹 code object 當(dāng)中主要的內(nèi)容,以及簡單介紹他們的作用,在后續(xù)的文章當(dāng)中會仔細(xì)分析 code object 對應(yīng)的源代碼以及對應(yīng)的字段的詳細(xì)作用。

現(xiàn)在舉一個例子來分析一下 pycdemo.py 的 pyc 文件,pycdemo.py 的源程序如下所示:

if __name__ == '__main__':
    a = 100
    print(a)

下面的代碼是一個用于加載 pycdemo01.cpython-39.pyc 文件(也就是 hello.py 對應(yīng)的 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)

執(zhí)行上面的程序輸出結(jié)果如下所示:

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 當(dāng)中各個字段的作用:

  • 首先需要了解一下代碼塊這個概念,所謂代碼塊就是一個小的 python 代碼,被當(dāng)做一個小的單元整體執(zhí)行。在 python 當(dāng)中常見的代碼塊塊有:函數(shù)體、類的定義、一個模塊。
  • argcount,這個表示一個代碼塊的參數(shù)個數(shù),這個參數(shù)只對函數(shù)體代碼塊有用,因?yàn)楹瘮?shù)可能會有參數(shù),比如上面的 pycdemo.py 是一個模塊而不是一個函數(shù),因此這個參數(shù)對應(yīng)的值為 0 。
  • co_code,這個對象的具體內(nèi)容就是一個字節(jié)序列,存儲真實(shí)的 python 字節(jié)碼,主要是用于 python 虛擬機(jī)執(zhí)行的,在本篇文章當(dāng)中暫時不詳細(xì)分析。
  • co_consts,這個字段是一個列表類型的字段,主要是包含一些字符串常量和數(shù)值常量,比如上面的 ";main" 和 100 。
  • co_filename,這個字段的含義就是對應(yīng)的源文件的文件名。
  • co_firstlineno,這個字段的含義為在 python 源文件當(dāng)中第一行代碼出現(xiàn)的行數(shù),這個字段在進(jìn)行調(diào)試的時候非常重要。
  • co_flags,這個字段的主要含義就是標(biāo)識這個 code object 的類型。0x0080 表示這個 block 是一個協(xié)程,0x0010 表示這個 code object 是嵌套的等等。
  • co_lnotab,這個字段的含義主要是用于計算每個字節(jié)碼指令對應(yīng)的源代碼行數(shù)。
  • co_varnames,這個字段的主要含義是表示在一個 code object 本地定義的一個名字。
  • co_names,和 co_varnames 相反,表示非本地定義但是在 code object 當(dāng)中使用的名字。
  • co_nlocals,這個字段表示在一個 code object 當(dāng)中本地使用的變量個數(shù)。
  • co_stackszie,因?yàn)?python 虛擬機(jī)是一個棧式計算機(jī),這個參數(shù)的值表示這個棧需要的最大的值。
  • co_cellvars,co_freevars,這兩個字段主要和嵌套函數(shù)和函數(shù)閉包有關(guān),我們在后續(xù)的文章當(dāng)中將詳細(xì)解釋這個字段。

總結(jié)

在本篇文章當(dāng)中主要給大家介紹了 python 文件被編譯之后的結(jié)果文件 .pyc 文件結(jié)構(gòu),在 pyc 文件當(dāng)中一個最重要的結(jié)構(gòu)就是 code object 對象,在本篇文章當(dāng)中主要是簡單介紹了 code object 各個字段的作用。在后續(xù)的文章當(dāng)中將會舉詳細(xì)的例子進(jìn)行說明,正確理解這些這些字段的含義,對于我們理解 python 虛擬機(jī)大有裨益。

本篇文章是深入理解 python 虛擬機(jī)系列文章之一,文章地址:github.com/Chang-LeHun…

更多精彩內(nèi)容合集可訪問項(xiàng)目:github.com/Chang-LeHun…

以上就是 python 虛擬機(jī):pyc 文件結(jié)構(gòu)的詳細(xì)內(nèi)容,更多關(guān)于 python 虛擬機(jī)pyc文件結(jié)構(gòu)的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • python入門:這篇文章帶你直接學(xué)會python

    python入門:這篇文章帶你直接學(xué)會python

    本教程并未涵蓋Python語言的全部內(nèi)容,只是一個入門的教程,Python有非常多的庫以及很多的功能特點(diǎn)需要學(xué)習(xí),小編只是拋磚引玉,希望大家可以從中受益
    2018-09-09
  • python數(shù)字圖像處理之高級濾波代碼詳解

    python數(shù)字圖像處理之高級濾波代碼詳解

    這篇文章主要介紹了python數(shù)字圖像處理之高級濾波代碼詳解,介紹了許多對圖像處理的濾波方法,具有一定參考價值,需要的朋友可以了解下。
    2017-11-11
  • python能自學(xué)嗎

    python能自學(xué)嗎

    在本篇文章里小編給大家整理了關(guān)于python如何自學(xué)的相關(guān)理論性文章,有需要的朋友們可以參考下。
    2020-06-06
  • python rsync服務(wù)器之間文件夾同步腳本

    python rsync服務(wù)器之間文件夾同步腳本

    這篇文章主要為大家詳細(xì)介紹了python rsync服務(wù)器之間文件夾同步腳本,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-08-08
  • Python圖像處理之幾何變換

    Python圖像處理之幾何變換

    這篇文章將詳細(xì)講解圖像幾何變換,包括圖像平移、圖像縮放和圖像旋轉(zhuǎn)。文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編學(xué)習(xí)一下
    2022-01-01
  • python使用html2text庫實(shí)現(xiàn)從HTML轉(zhuǎn)markdown的方法詳解

    python使用html2text庫實(shí)現(xiàn)從HTML轉(zhuǎn)markdown的方法詳解

    這篇文章主要介紹了python使用html2text庫實(shí)現(xiàn)從HTML轉(zhuǎn)markdown的方法,需要的朋友可以參考下
    2020-02-02
  • Python使用flask框架操作sqlite3的兩種方式

    Python使用flask框架操作sqlite3的兩種方式

    這篇文章主要介紹了Python使用flask框架操作sqlite3的兩種方式,結(jié)合實(shí)例形式分析了Python基于flask框架操作sqlite3數(shù)據(jù)庫的兩種常用操作技巧,需要的朋友可以參考下
    2018-01-01
  • python中字符串的操作方法大全

    python中字符串的操作方法大全

    這篇文章主要給大家介紹了關(guān)于python中字符串操作方法的相關(guān)資料,文中通過示例代碼詳細(xì)介紹了關(guān)于python中字符串的大小寫轉(zhuǎn)換、isXXX判斷、填充、子串搜索、替換、分割、join以及修剪:strip、lstrip和rstrip的相關(guān)內(nèi)容,需要的朋友可以參考下
    2018-06-06
  • 基于Python實(shí)現(xiàn)對Excel工作表中的數(shù)據(jù)進(jìn)行排序

    基于Python實(shí)現(xiàn)對Excel工作表中的數(shù)據(jù)進(jìn)行排序

    在Excel中,排序是整理數(shù)據(jù)的一種重要方式,它可以讓你更好地理解數(shù)據(jù),本文將介紹如何使用第三方庫Spire.XLS?for?Python通過Python來對Excel中的數(shù)據(jù)進(jìn)行排序,需要的可以參考下
    2024-03-03
  • python selenium 無界面瀏覽器的實(shí)現(xiàn)

    python selenium 無界面瀏覽器的實(shí)現(xiàn)

    有時我們不想讓瀏覽器窗口跳出來,而是想在后臺進(jìn)行操作,這就需要用到無界面瀏覽器,本文主要介紹了python selenium 無界面瀏覽器的實(shí)現(xiàn),具有一定的參考價值,感興趣的可以了解一下
    2023-10-10

最新評論