<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          當(dāng)我print時(shí),Python做了什么

          共 3653字,需瀏覽 8分鐘

           ·

          2020-08-28 02:59

          文 |?軒轅御龍

          來源:Python 技術(shù)「ID: pythonall」



          寫了這么久的程序,不知道大家有沒有思考過,Python到底在干嘛呢?

          或者換句話說,當(dāng)我們執(zhí)行Python代碼的時(shí)候,是怎么實(shí)現(xiàn)的呢?

          眾所周知,Python是一門解釋型的語言

          ——所謂“解釋型”,當(dāng)然是區(qū)別于以C語言為代表的編譯型語言。編譯型語言需要將整個(gè)程序文件全部轉(zhuǎn)換為可以直接由機(jī)器執(zhí)行的二進(jìn)制文件;而解釋型語言則是由相應(yīng)的解釋器一行一行“解釋”并執(zhí)行代碼描述的行為。

          正是因此,對(duì)于新接觸的人來說,Python這樣的解釋性語言很多時(shí)候需要執(zhí)行到相應(yīng)的語句,才會(huì)發(fā)現(xiàn)一些顯然的錯(cuò)誤。

          話說回來,Python的解釋器是怎么樣來“解釋”Python代碼的呢?

          實(shí)際上,類似于Java的執(zhí)行機(jī)制,Python也擁有自己的虛擬機(jī)。而這個(gè)虛擬機(jī)實(shí)際上執(zhí)行的也是一種“字節(jié)碼”。

          在Python程序的執(zhí)行中依然存在一個(gè)“編譯”的過程:將Python代碼編譯為字節(jié)碼。

          并且,Python也提供了一個(gè)名為dis模塊,用于查看、分析Python的字節(jié)碼。

          1. dis模塊

          舉例來說,dis模塊中有一個(gè)同名函數(shù)dis,可以用于將當(dāng)前命名空間中的對(duì)象反匯編為字節(jié)碼。

          import dis
          def add(add_1, add_2): sum_value = add_1 + add_2
          dis.dis(add)

          執(zhí)行結(jié)果為:

            4           0 LOAD_FAST                0 (add_1)              2 LOAD_FAST                1 (add_2)              4 BINARY_ADD              6 STORE_FAST               2 (sum_value)              8 LOAD_CONST               0 (None)             10 RETURN_VALUE

          其中,開頭的數(shù)字“4”表示字節(jié)碼的內(nèi)容對(duì)應(yīng)于腳本中第4行的內(nèi)容。

          隨后的一列數(shù)字則表示對(duì)應(yīng)指令所在的地址。縱向觀察可以發(fā)現(xiàn)一個(gè)規(guī)律:下一條指令的地址總比上一條指令的地址大2。這是巧合嗎?

          顯然不是的。官方文檔《dis --- Python 字節(jié)碼反匯編器》中記錄的更改顯示,從Python 3.6版本開始,”每條指令使用2個(gè)字節(jié)“。所以每條指令的地址會(huì)在上一條指令地址的基礎(chǔ)上加2。

          再往后,是一列表示指令含義的單詞組合,實(shí)際上就是人類可讀的對(duì)應(yīng)指令名稱。顧名思義,LOAD_FAST就是加載某個(gè)內(nèi)容/對(duì)象到某處,”FAST“很可能意味著這是一個(gè)便捷快速的命令實(shí)現(xiàn)。

          最右邊,則是對(duì)應(yīng)于當(dāng)前命令的操作數(shù),即操作對(duì)象。數(shù)字同樣是一個(gè)類似于地址的表示,括號(hào)中的字符串則表示相應(yīng)對(duì)象在Python代碼中的具體名稱。

          這樣我們就可以大概地閱讀生成的字節(jié)碼了:

          首先Python將函數(shù)add的第一個(gè)參數(shù)add_1加載到某處,緊跟著將第二個(gè)參數(shù)add_2加載到第一個(gè)參數(shù)之后。然后調(diào)用了一個(gè)名為BINARY_ADD的指令,即對(duì)之前加載的兩個(gè)參數(shù)做加法。再然后則是將加法所得的和sum_value存儲(chǔ)在了另一個(gè)位置。最后,加載了一個(gè)常量None并返回。

          其實(shí)讀完上面這個(gè)執(zhí)行過程,我們很容易想到一種常用的數(shù)據(jù)結(jié)構(gòu)——棧。

          像下面這樣:

          01

          當(dāng)然這并不是本文的重點(diǎn)——真要探討Python的實(shí)現(xiàn)機(jī)制,還得另外寫幾篇長文才能說得一二。

          使用dis.dis函數(shù)除了可以查看當(dāng)前腳本中各個(gè)對(duì)象對(duì)應(yīng)的字節(jié)碼,還可以直接傳入一段代碼對(duì)應(yīng)的字符串進(jìn)行反匯編:

          # test_dis.pyimport dis

          s = """def add(add_1, add_2): sum_value = add_1 + add_2
          print("Hello World!")
          import sys"""
          dis.dis(s)

          匯編結(jié)果:

            2           0 LOAD_CONST               0 (", line 2>)              2 LOAD_CONST               1 ('add')              4 MAKE_FUNCTION            0              6 STORE_NAME               0 (add)
          5 8 LOAD_NAME 1 (print) 10 LOAD_CONST 2 ('Hello World!') 12 CALL_FUNCTION 1 14 POP_TOP
          7 16 LOAD_CONST 3 (0) 18 LOAD_CONST 4 (None) 20 IMPORT_NAME 2 (sys) 22 STORE_NAME 2 (sys) 24 LOAD_CONST 4 (None) 26 RETURN_VALUE

          2. compile函數(shù)

          除了在程序中直接給出要反匯編的程序形成的字符串,我們還可以通過使用內(nèi)置函數(shù)compile來形成相應(yīng)腳本的編譯對(duì)象,再使用dis.dis查看其字節(jié)碼內(nèi)容。

          # test_compile.pyimport dis
          with open("test_dis.py", "r", encoding="utf-8") as f: s = f.read()
          compile_obj = compile(s, "test_dis.py","exec")
          dis.dis(compile_obj)

          字節(jié)碼輸出結(jié)果:

            1           0 LOAD_CONST               0 (0)              2 LOAD_CONST               1 (None)              4 IMPORT_NAME              0 (dis)              6 STORE_NAME               0 (dis)
          11 8 LOAD_CONST 2 ('\ndef add(add_1, add_2):\n sum_value = add_1 + add_2\n\nprint("Hello World!")\n\nimport sys\n') 10 STORE_NAME 1 (s)
          13 12 LOAD_NAME 0 (dis) 14 LOAD_METHOD 0 (dis) 16 LOAD_NAME 1 (s) 18 CALL_METHOD 1 20 POP_TOP 22 LOAD_CONST 1 (None) 24 RETURN_VALUE

          總結(jié)

          dis模塊為我們提供了一個(gè)觀察Python內(nèi)部機(jī)制的手段,恰當(dāng)?shù)厥褂?code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">dis模塊,并結(jié)合其他方法,可以快速有效弄懂一些Python令人迷惑的地方。

          希望大家善于利用這樣一些有用的工具。

          參考

          [dis --- Python 字節(jié)碼反匯編器]https://docs.python.org/zh-cn/3/library/dis.html

          [談?wù)?Python 程序的運(yùn)行原理]https://www.cnblogs.com/restran/p/4903056.html

          PS公號(hào)內(nèi)回復(fù)「Python」即可進(jìn)入Python 新手學(xué)習(xí)交流群,一起 100 天計(jì)劃!


          老規(guī)矩,兄弟們還記得么,右下角的 “在看” 點(diǎn)一下如果感覺文章內(nèi)容不錯(cuò)的話,記得分享朋友圈讓更多的人知道!

          代碼獲取方式

          識(shí)別文末二維碼,回復(fù):200827

          瀏覽 51
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  91视频人人爱 | 日韩a在线观看 | 欧美一级特黄真人做受 | 日韩婷婷五月天亚洲黄色视频 | 男女激情操逼一区福利网站 |