最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

 更新時(shí)間: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)中的一個(gè)核心內(nèi)容就是 python 字節(jié)碼。

PYC 文件

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

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

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

生成 PYC 文件

正常的 python 文件需要通過編譯器變成字節(jié)碼,然后將字節(jié)碼交給 python 虛擬機(jī),然后 python 虛擬機(jī)會(huì)執(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ù)是由一個(gè) 2 字節(jié)的整數(shù)和另外兩個(gè)字符回車換行組成的, "\r\n" 也占用兩個(gè)字節(jié),一共是四個(gè)字節(jié)。這個(gè)兩個(gè)字節(jié)的整數(shù)在不同的 python 版本還不一樣,比如說在 python3.5 當(dāng)中這個(gè)值為 3351 等值,在 python3.9 當(dāng)中這個(gè)值為 3420,3421,3422,3423,3424等值(在 python 3.9 的小版本)。

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

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

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

我們現(xiàn)在來具體分析一個(gè) 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 這個(gè)文件的大小是 32 字節(jié)。

下面是一個(gè)小的代碼片段用于讀取 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 是一個(gè)對象,它包含了 Python 代碼的字節(jié)碼、常量、變量、位置參數(shù)、關(guān)鍵字參數(shù)等信息,以及一些用于運(yùn)行代碼的元數(shù)據(jù),如文件名、代碼行號等。

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

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

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

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

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

下面的代碼是一個(gè)用于加載 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)中各個(gè)字段的作用:

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

總結(jié)

在本篇文章當(dāng)中主要給大家介紹了 python 文件被編譯之后的結(jié)果文件 .pyc 文件結(jié)構(gòu),在 pyc 文件當(dāng)中一個(gè)最重要的結(jié)構(gòu)就是 code object 對象,在本篇文章當(dāng)中主要是簡單介紹了 code object 各個(gè)字段的作用。在后續(xù)的文章當(dāng)中將會(huì)舉詳細(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加密與解密模塊hashlib與hmac

    Python加密與解密模塊hashlib與hmac

    這篇文章介紹了Python中的加密與解密模塊hashlib與hmac,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-06-06
  • 關(guān)于pandas.date_range()的用法及說明

    關(guān)于pandas.date_range()的用法及說明

    這篇文章主要介紹了關(guān)于pandas.date_range()的用法及說明,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • Python爬取英雄聯(lián)盟MSI直播間彈幕并生成詞云圖

    Python爬取英雄聯(lián)盟MSI直播間彈幕并生成詞云圖

    很開心RNG最近在英雄聯(lián)盟季中賽奪冠了,特地爬取了直播間彈幕并生成詞云圖,大家一起開心一下,看看奪冠時(shí)大家都在說什么,需要的朋友可以參考下
    2021-06-06
  • python數(shù)學(xué)建模之三大模型與十大常用算法詳情

    python數(shù)學(xué)建模之三大模型與十大常用算法詳情

    這篇文章主要介紹了python數(shù)學(xué)建模之三大模型與十大常用算法詳情,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,感想取得小伙伴可以參考一下
    2022-07-07
  • djang常用查詢SQL語句的使用代碼

    djang常用查詢SQL語句的使用代碼

    這篇文章主要介紹了djang常用查詢SQL語句的使用代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-02-02
  • OpenCV之理解KNN鄰近算法k-Nearest?Neighbour

    OpenCV之理解KNN鄰近算法k-Nearest?Neighbour

    這篇文章主要為大家介紹了OpenCV之理解KNN鄰近算法k-Nearest?Neighbour,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-05-05
  • 利用python進(jìn)行接口測試及類型介紹

    利用python進(jìn)行接口測試及類型介紹

    這篇文章主要介紹了利用python進(jìn)行接口測試詳情,文章基于python展開對接口測試的詳細(xì)介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-05-05
  • Python 集合之set詳解

    Python 集合之set詳解

    這篇文章主要介紹了python基礎(chǔ)之set集合詳解,文中有非常詳細(xì)的代碼示例,對正在學(xué)習(xí)python的小伙伴們有很好地幫助,需要的朋友可以參考下
    2021-09-09
  • python使用多線程查詢數(shù)據(jù)庫的實(shí)現(xiàn)示例

    python使用多線程查詢數(shù)據(jù)庫的實(shí)現(xiàn)示例

    這篇文章主要介紹了python使用多線程查詢數(shù)據(jù)庫的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • 用Python編寫一個(gè)簡單的CS架構(gòu)后門的方法

    用Python編寫一個(gè)簡單的CS架構(gòu)后門的方法

    今天小編就為大家分享一篇關(guān)于用Python編寫一個(gè)簡單的CS架構(gòu)后門的方法,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2018-11-11

最新評論

台中县| 疏勒县| 迁安市| 呼和浩特市| 类乌齐县| 体育| 卢龙县| 平昌县| 会昌县| 光山县| 句容市| 大埔区| 丰都县| 白山市| 奉节县| 沧源| 会同县| 西宁市| 信丰县| 广安市| 梅河口市| 兰州市| 乾安县| 东乌珠穆沁旗| 通州市| 湛江市| 伽师县| 渭源县| 武邑县| 鸡西市| 大渡口区| 拉萨市| 永定县| 舞钢市| 鹤岗市| 星子县| 林西县| 房产| 安国市| 温泉县| 个旧市|