# Python 執行原理 Byte Code
電腦不能夠識別高級語言,所以需要一個翻譯機把高級語言轉變成電腦能識別的機器語言,這個過程分成編譯型語言與解釋型語言。
* 編譯型語言在執行程序前,會先通過一個編譯的過程,編釋器(compiler)把程序轉變成機器語言,運行時就不需翻譯就可以執行,如C語言。
* 直譯型(解釋型)語言就是不透過編譯的過程,而是在程序運行時,通過解釋器(intepreter)對程序逐行翻譯,再直接運行,如 Ruby 跟 Python。
### *.py 執行過程
以 Python3 為例,執行一個python3 file.py 檔,此時發生:
1. Interpreter 會將 Python Source Code file.py interpret 成 Byte Code(儲存 Byte 型態的資料)。
2. Byte Code 會再被 Virtual Machine compile 成 Machine Code 以及 Executable Code 等。
3. 最後Virtual Machine 會觸發 CPU 以及系統調度來執行這份程式碼要處理的事。
**示意圖:**

### Byte Code
為什麼設計這樣的流程產生一個中間程式碼(Byte Code),而不直接生成Excutable File?
提供更靈活架構方便轉換、提昇嫁接性,如 Byte Code 可以對接 Java 的 Virtual Machine,達成跨平台、系統的效果。
可作到「一行一行執行」的概念,不花費許多資源做通到底的編譯,提昇開發效率。
常見支援 Python 的 Interpreters :
CPython (C 寫成): 最多人使用
PyPy (RPython 寫成)
Jython (Java 寫成)
### *.pyc 用途
Byte Code 除了被 C Virtual Machine 讀取並執行外,Python 會把常使用的 Byte Code 寫進 Disk 儲存起來,變成 *.pyc 檔。通常在__pycache__資料夾下,import的library會在LIB
pyc是一種二進位制檔案,是由py檔經過直譯後生成的檔案,是 Byte Code 在 Python 裡的一個實現。pyc的內容跟python版本相關,不同版本直譯後的pyc檔案不同。
為什麼需要 .pyc 檔?
主要的理由是加速運行,py檔案變成pyc檔案後,提高載入的速度。而且pyc是一種跨平臺的位元組碼,由python的虛擬機器來執行。
*.pyc 參與Python執行過程:
* 當 Python 程序運行時,解釋的結果會暫時保存在記憶體中的 PyCodeObject中,當 Python 程序結束時,Python解釋器會把PyCodeObject寫入到pyc的檔案裡。
* 當 Python 程序第二次運行時,首先會在硬碟中尋找 .pyc檔,如果有找到,並且發現Python的原始碼被異動,就會檢查 *.py文件的更新時間與 *.pyc 文件的更新時間,如果 *.py的時間比較新,代表檔案有更新,則重複解釋器的過程;反之,就直接載入。
### *py轉成 *.pyc
py_compile 模組把py檔案編譯為pyc檔案
#### 生成單個*.pyc
把某py檔案編譯為pyc檔案。
```python=
import py_compile
py_compile.compile(r'hi.py')
print('done')
```
函式compile(file, cfile, dfile, doraise)說明:
* file:需要編譯的py檔案的路徑。
* cfile:表示編譯後的pyc檔名和路徑,預設為直接在file檔名後加c或o,o表示優化的位元組碼。
* dfile:把在錯誤資訊中顯示的file名稱用dfile名稱替換。
* doraise:True或False,如果為True,如果編譯檔案出錯會引發一個PyCompileError;否則預設顯示在sys.stderr中,而不會引發異常。
* optimize:用於編譯的最佳化層級,有效值為-1,0,1,2。-1表示當前解譯器的最佳化層級。
#### 批量生成*.pyc
把某目錄及其子目錄下的py檔案編譯為pyc檔案。
用命令列編譯一個目錄下的檔案,如:python -m compileall /root/src/
```python=
import compileall
compileall.compile_dir(r'D:\game')
print('done')
```
函式compile_dir(dir, maxlevels, ddir, force, rx, quiet)說明:
* dir:需要編譯的資料夾位置。
* maxlevels:需要遞迴編譯的子目錄的層數,預設是10層。
* ddir:把在錯誤資訊中顯示的file用ddir替換。
* force:如果為True,即使現在的pyc檔案是最新的,也會再強制編譯一次,pyc檔案中包含時間戳,python編譯器會根據時間來決定,是否需要重新生成一次pyc檔案。
* rx:為正則表示式,可過濾符合條件的目錄進行編譯。
* quiet:如果為True,則編譯後不會在標準輸出中印出資訊。
修改python原始碼中的opcode檔案(Python39\include\opcode.h & Python39\Lib\opcode.py),新生成的pyc檔,將無法被其他版本python所使用,可防止被反編譯破解。
如何使用修改後的opcode?
更改opcode的相關檔案,再安裝python,刪除舊有的pyc檔,便可使用不同opcode的python環境。
**Reference:**
https://www.796t.com/content/1508990186.html
https://saucer-man.com/information_security/825.html
https://medium.com/citycoddee/python%E9%80%B2%E9%9A%8E%E6%8A%80%E5%B7%A7-5-python-%E5%88%B0%E5%BA%95%E6%80%8E%E9%BA%BC%E8%A2%AB%E5%9F%B7%E8%A1%8C-%E7%9B%B4%E8%AD%AF-%E7%B7%A8%E8%AD%AF-%E5%AD%97%E7%AF%80%E7%A2%BC-%E8%99%9B%E6%93%AC%E6%A9%9F%E7%9C%8B%E4%B8%8D%E6%87%82-553182101653