常見(jiàn)的 Python 文件后綴有:py
、pyc
、pyo
、 pyi
、pyw
、 pyd
、 pyx
等。
本文只介紹相對(duì)常見(jiàn)的一些后綴名,至于一些特別冷門的文件格式,例如一些文章提到的pyz
、pywz
、rpy
、pyde
、pyp
、 pyt
等,并沒(méi)有進(jìn)行研究。因?yàn)檫@些擴(kuò)展名資料很少,網(wǎng)上搜到的文章似乎都是同一個(gè)出處,只是簡(jiǎn)單提了一句,說(shuō)了等于沒(méi)說(shuō)。
py
最常見(jiàn)的 Python 源代碼文件。
實(shí)際上如果用 python + 文件
的方式運(yùn)行代碼,只要文件內(nèi)容相同,后綴名是不重要的,也就是說(shuō)下面的運(yùn)行結(jié)果都是等價(jià)的:
python test.py
python test.txt
python test
pyc
常見(jiàn)的 Python 字節(jié)碼緩存文件。
pyc
文件和py
文件一樣,都可以直接執(zhí)行,下面的運(yùn)行結(jié)果都是等價(jià)的:
python test.py
python test.pyc
作用一:提升加載性能
我們知道 Python 代碼在執(zhí)行時(shí),會(huì)先由 Python 解析器翻譯成 PyCodeObject 對(duì)象,俗稱字節(jié)碼 (Byte code),然后交給 Python 解釋器來(lái)執(zhí)行字節(jié)碼。
上述過(guò)程中翻譯后的字節(jié)碼是保存在內(nèi)存中,程序運(yùn)行結(jié)束就沒(méi)了,而代碼沒(méi)有修改的情況下,每次生成的字節(jié)碼是一樣的,所以每次跑程序都再走一遍翻譯字節(jié)碼的過(guò)程有點(diǎn)浪費(fèi)性能。因此為了提高加載效率,Python 在程序執(zhí)行結(jié)束后會(huì)把每個(gè)文件的字節(jié)碼寫(xiě)入到硬盤(pán)中保存為 xxx.pyc
文件,這樣下一次再執(zhí)行這個(gè)程序時(shí)先在目錄下找有沒(méi)有xxx.pyc
文件,如果有這個(gè)對(duì)應(yīng)文件且修改時(shí)間和xxx.py
文件的修改時(shí)間一樣,就不用再執(zhí)行翻譯成字節(jié)碼的過(guò)程,直接讀取xxx.pyc
文件執(zhí)行。其實(shí)緩存pyc
文件的方式對(duì)性能的提升很微小,只有項(xiàng)目文件非常多的時(shí)候才能看到顯著提升。
默認(rèn)情況下,我們發(fā)現(xiàn)并不是所有的py
文件都會(huì)自動(dòng)生成pyc
文件,只有被其他文件 import 過(guò)的文件才會(huì)生成對(duì)應(yīng)的pyc
文件??赡?Python 認(rèn)為被 import 的文件重復(fù)使用的概率比較高,而主文件一般只需要加載一次。
簡(jiǎn)單做個(gè)實(shí)驗(yàn)可以驗(yàn)證,新建兩個(gè) Python 文件hello.py
和import.py
,內(nèi)容如下:
# hello.py
print("hello")
直接運(yùn)行 python hello.py
,并沒(méi)有生成pyc
文件,而運(yùn)行python import.py
,在當(dāng)前目錄下生成了hello.py
對(duì)應(yīng)的pyc
文件。
這里 Python2 和 Python3 有些不同, Python2 是直接在當(dāng)前目錄下生成同名 pyc
文件,Python3 是在當(dāng)前目錄下創(chuàng)建了__pycache__
文件夾,然后在文件夾內(nèi)創(chuàng)建了一個(gè)包含 Python 版本信息的xxx.cpython-37.pyc
文件。
Python2
Python3
作用二:隱藏源代碼
pyc
格式是給解釋器看的二進(jìn)制文件,直接用編輯器打開(kāi)看上去是亂碼,所以將 Python 代碼先編譯成pyc
文件再交付給別人使用,一定程度上實(shí)現(xiàn)隱藏源代碼的效果。
默認(rèn)情況下,主文件不會(huì)生成pyc
文件,可以通過(guò) Python 自帶的py_compile
或compileall
庫(kù),手動(dòng)將所有py
文件"編譯"成pyc
文件。
python -m py_compile *.py
python -m compileall *.py
Python2
Python3
反編譯 pyc
前面說(shuō)了,是“一定程度上實(shí)現(xiàn)隱藏源代碼的效果”,其實(shí)可以通過(guò)反編譯pyc
文件來(lái)獲得py
源碼,而且反編譯的難度并不大。
uncompyle6
是一個(gè)專門用于將pyc
反編譯為py
源碼的第三方庫(kù),安裝方式:
執(zhí)行下面命令可以將剛才生成的pyc
反編譯為py
文件:
打開(kāi)生成的文件hello.cpython-37.py
和import.cpython-37.py
,可以看到和之前的py
代碼內(nèi)容一模一樣,不過(guò)多了一些 Python 的版本信息。
魔高一尺,道高一丈,有反編譯技術(shù)就有防止反編譯技術(shù),更多了解參見(jiàn)這篇文章:通過(guò)字節(jié)碼混淆來(lái)保護(hù)Python代碼。
pyo
優(yōu)化后的 Python 字節(jié)碼緩存文件。
pyo
文件的作用和pyc
文件沒(méi)啥區(qū)別,唯一的優(yōu)化就是去掉了斷言語(yǔ)句,即assert
語(yǔ)句。官方文檔描述:
When the Python interpreter is invoked with the -O flag, optimized code is generated and stored in .pyo files. The optimizer currently doesn't help much; it only removes assert statements. When -O is used, all bytecode is optimized; .pyc files are ignored and .py files are compiled to optimized bytecode.
同樣可以利用py_compile
或compileall
庫(kù)將上面示例的兩個(gè)文件編譯成pyo
文件,只是多加一個(gè)參數(shù)-O
,運(yùn)行結(jié)果也沒(méi)有任何變化:
python -O -m py_compile *.py
python -O -m compileall *.py
從 Python3.5 開(kāi)始,Python 只使用 pyc
而不再使用pyo
,所以下面命令也無(wú)法生成 pyo
文件,生成的依然是 pyc
文件:
python3 -O -m py_compile *.py
python3 -O -m compileall *.py
pyi
Python 的存根文件,用于代碼檢查時(shí)的類型提示。
pyi
文件是PEP484
提案規(guī)定的一種用于 Python 代碼類型提示(Type Hints)的文件。PEP
即Python Enhancement Proposals
,是經(jīng)過(guò) Python 社區(qū)核心開(kāi)發(fā)者討論并一致同意后,對(duì)外發(fā)布的一些正式規(guī)范文檔,例如我們常說(shuō)的Python之禪(PEP20
),代碼風(fēng)格 PEP8 格式化(PEP8
),將 print 改為函數(shù)(PEP3105
)等,關(guān)于PEP
的更多了解見(jiàn)這篇文章:學(xué)習(xí)Python,怎能不懂點(diǎn)PEP呢?。
常用的 IDE 都會(huì)有類型檢查提示功能,比如在 PyCharm 中,當(dāng)我們給一個(gè)函數(shù)傳入一個(gè)錯(cuò)誤的類型時(shí)會(huì)給出對(duì)應(yīng)的提示,這其實(shí)不是 IDE 的特殊開(kāi)發(fā)的功能,它只是集成了PEP484
的規(guī)定,利用了已經(jīng)預(yù)先生成好的 pyi
文件。
舉個(gè)例子,os.makedirs
是標(biāo)準(zhǔn)庫(kù)中用于創(chuàng)建文件夾路徑的函數(shù),它的入?yún)?yīng)該是一個(gè)字符串類型,如果傳入一個(gè) int 類型,IDE 會(huì)立刻給出提示。
按住ctrl
點(diǎn)進(jìn)去,進(jìn)入到 os 模塊定義os.makedirs
的地方,發(fā)現(xiàn)前面有個(gè)*
號(hào),鼠標(biāo)放上去會(huì)提示Has stub item in __init__.pyi
。
點(diǎn)擊*
號(hào)就會(huì)跳到對(duì)應(yīng)的__init__.pyi
文件,這個(gè)文件里按照PEP484
規(guī)定,為os
模塊每個(gè)函數(shù)都定義了對(duì)應(yīng)的類型檢查規(guī)則。
關(guān)于pyi
文件的定義規(guī)則以及自己如何生成,詳見(jiàn)官方文檔:PEP 484 – Type Hints
pyw
一種 Python 源代碼文件,一般只存在于 Windows 系統(tǒng)。
pyw
文件和py
文件除了后綴名不一樣之外沒(méi)有任何區(qū)別,兩者都是 Python 源碼文件,前面 py
那一節(jié)說(shuō)過(guò)“如果用 python + 文件
的方式運(yùn)行代碼,只要文件內(nèi)容相同,后綴名是不重要的”,這一點(diǎn)在 Windows 系統(tǒng)和 Linux 系統(tǒng)都是一樣的。
Windows 系統(tǒng),新建兩個(gè)內(nèi)容相同的 Python 文件hello.py
和hello.pyw
,用python + 文件
的方式運(yùn)行,結(jié)果一樣:
# hello.py
print("hello")
# hello.pyw
print("hello")
那為什么還要有pyw
文件呢?
在Windows 系統(tǒng)上雙擊文件時(shí),系統(tǒng)會(huì)根據(jù)文件擴(kuò)展名來(lái)調(diào)用關(guān)聯(lián)的exe
程序來(lái)運(yùn)行這個(gè)文件,打開(kāi) Python 安裝目錄,可以看到有python.exe
和pythonw.exe
兩個(gè)exe
,其中python.exe
關(guān)聯(lián)了py
文件,pythonw.exe
關(guān)聯(lián)了pyw
文件。跟 python.exe
相比,pythonw.exe
運(yùn)行時(shí)不會(huì)彈出控制臺(tái)窗口, stdout 、stderr 和 stdin 都無(wú)效,所以像 print 這種把內(nèi)容輸出到 stdout 的操作就不會(huì)有打印結(jié)果(cmd 窗口都沒(méi)有了也沒(méi)有地方顯示了)。
所以在用 Python 開(kāi)發(fā) GUI 程序時(shí),如果不想讓程序運(yùn)行的時(shí)候彈出一個(gè)黑乎乎的 cmd 框,就可以將源碼文件后綴名改為pyw
格式。但是我感覺(jué)這個(gè)pww
格式用處并不大,實(shí)際使用很少有人雙擊py
或者pyw
文件來(lái)運(yùn)行 Python 代碼。我之前曾用tkinter
開(kāi)發(fā)過(guò)帶 Windows 界面的 Python 程序,當(dāng)時(shí)是通過(guò)雙擊 bat
腳本啟動(dòng) Python 腳本同時(shí)關(guān)閉 cmd 界面框,來(lái)避免彈出黑框框的。
pyd
Python 可直接調(diào)用的 C 語(yǔ)言動(dòng)態(tài)鏈接庫(kù)文件,一般只存在于 Windows 系統(tǒng)。
Python 是一種膠水語(yǔ)言,我們可以將對(duì)速度要求比較高的那一部分代碼使用 C 語(yǔ)言編寫(xiě),編譯成動(dòng)態(tài)鏈接庫(kù)文件,再通過(guò) Python 來(lái)調(diào)用。一般來(lái)說(shuō),在 Linux 上是 so
文件,在 Windows 系統(tǒng)上是DLL
文件。
例如有一個(gè) C 語(yǔ)言編寫(xiě)的 Windows 動(dòng)態(tài)鏈接庫(kù) test_lib.dll
,編譯前的代碼如下:
int sum(int x, int y)
{
return x + y;
}
我們可以在 Python 代碼中通過(guò)下面的方式來(lái)調(diào)用
# test_lib.dll 放在當(dāng)前目錄下
import ctypes
from ctypes import *
test_lib = ctypes.windll.LoadLibrary("test_lib.dll")
a = ctypes.c_int(1)
b = ctypes.c_int(2)
out = test_lib.sum(a, b)
print(out) # 3
在 Windows 系統(tǒng)上,Python 還有一種 pyd
格式的動(dòng)態(tài)鏈接庫(kù),上面的調(diào)用方式是先通過(guò)ctypes.windll.LoadLibrary
方法將動(dòng)態(tài)鏈接庫(kù)加載進(jìn)來(lái),而pyd
格式就可以在 Python 代碼中直接import
進(jìn)來(lái),類似下面這樣:
# test_lib.pyd 放在當(dāng)前目錄下
import test_lib
out = test_lib.sum(1, 2)
print(out) # 3
關(guān)于 pyd
文件和dll
文件的區(qū)別,可參考官方文檔的說(shuō)明:
Is a *.pyd
file the same as a DLL?
Yes, .pyd files are dll's, but there are a few differences. If you have a DLL named foo.pyd
, then it must have a function PyInit_foo()
. You can then write Python “import foo”, and Python will search for foo.pyd (as well as foo.py, foo.pyc) and if it finds it, will attempt to call PyInit_foo()
to initialize it. You do not link your .exe with foo.lib, as that would cause Windows to require the DLL to be present.
Note that the search path for foo.pyd is PYTHONPATH, not the same as the path that Windows uses to search for foo.dll. Also, foo.pyd need not be present to run your program, whereas if you linked your program with a dll, the dll is required. Of course, foo.pyd is required if you want to say import foo
. In a DLL, linkage is declared in the source code with __declspec(dllexport)
. In a .pyd, linkage is defined in a list of available functions.
C 語(yǔ)言代碼和 Python 代碼都可以通過(guò)一定的方法編譯成pyd
格式的文件,本人并沒(méi)有實(shí)際使用過(guò)pyd
文件
PyTorch中的C++擴(kuò)展實(shí)現(xiàn) https://www.jb51.net/article/184030.htm
Python文件編譯生成pyd/so庫(kù) https://www.jb51.net/article/148711.htm
pyx
Cython 源代碼文件。
注意是 Cython 不是 CPython。Cython 可以說(shuō)是一種編程語(yǔ)言, 它結(jié)合了Python 的語(yǔ)法和有 C/C++的效率,用 Cython 寫(xiě)完的代碼可以很容易轉(zhuǎn)成 C 語(yǔ)言代碼,然后又可以再編譯成動(dòng)態(tài)鏈接庫(kù)(pyd
或dll
)供 Python 調(diào)用,所以 Cython 一般用來(lái)編寫(xiě) Python 的 C 擴(kuò)展,上面說(shuō)的 Python 文件編譯生成 pyd
文件就是利用 Cython 來(lái)實(shí)現(xiàn)的 。Cython 的源代碼文件一般為pyx
后綴。
總結(jié)
后綴名 |
作用 |
py |
最常見(jiàn)的 Python 源代碼文件。 |
pyc |
常見(jiàn)的 Python 字節(jié)碼緩存文件,可以反編譯成 py 文件。 |
pyo |
另一種 Python 字節(jié)碼緩存文件,只存在于 Python2 及 Python3.5 之前的版本。 |
pyi |
Python 的存根文件,常用于 IDE 代碼格式檢查時(shí)的類型提示。 |
pyw |
另一種 Python 源代碼文件,一般只存在于 Windows 系統(tǒng)。 |
pyd |
一種 Python 可直接調(diào)用的 C 語(yǔ)言動(dòng)態(tài)鏈接庫(kù)文件,一般只存在于 Windows 系統(tǒng)。 |
pyx |
Cython 源代碼文件,一般用來(lái)編寫(xiě) Python 的 C 擴(kuò)展。 |
到此這篇關(guān)于Python 相關(guān)文件常見(jiàn)的后綴名詳解的文章就介紹到這了,更多相關(guān)Python 文件后綴名內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
您可能感興趣的文章:- Python實(shí)現(xiàn)的批量修改文件后綴名操作示例
- python文件操作之批量修改文件后綴名的方法
- python 拷貝特定后綴名文件,并保留原始目錄結(jié)構(gòu)的實(shí)例
- python3 遍歷刪除特定后綴名文件的方法
- python獲取文件路徑、文件名、后綴名的實(shí)例
- python獲取文件后綴名及批量更新目錄下文件后綴名的方法