Python Zip 導(dǎo)入:快速分發(fā)模塊和包
目錄
了解 Python Zip 導(dǎo)入
創(chuàng)建您自己的可導(dǎo)入 ZIP 文件
將 Python 模塊捆綁到 ZIP 文件中
將 Python 包捆綁到 ZIP 文件中
了解 Zip 導(dǎo)入的限制
從 ZIP 文件導(dǎo)入 Python 代碼
動態(tài)使用 sys.path 進(jìn)行 Zip 導(dǎo)入
使用 PYTHONPATH 進(jìn)行系統(tǒng)范圍的 Zip 導(dǎo)入
使用 .pth 文件進(jìn)行解釋器范圍的 Zip 導(dǎo)入
探索 Python 的 zipimport:Zip 導(dǎo)入背后的工具
了解 zipimport 的基礎(chǔ)知識
使用 zipimport 構(gòu)建插件系統(tǒng)
結(jié)論
Python 允許您直接通過Zip導(dǎo)入從 ZIP 文件導(dǎo)入代碼。這個有趣的內(nèi)置功能使您可以壓縮 Python 代碼以進(jìn)行分發(fā)。如果您經(jīng)常使用 ZIP 文件中的 Python 代碼,Zip 導(dǎo)入也會有所幫助。無論哪種情況,學(xué)習(xí)創(chuàng)建可導(dǎo)入的 ZIP 文件并從中導(dǎo)入代碼都是一項寶貴的技能。
即使您的日常工作流不涉及包含 Python 代碼的 ZIP 文件,您仍將通過本教程探索 Zip 導(dǎo)入來學(xué)習(xí)一些有趣和有趣的新技能。
在本教程中,您將學(xué)習(xí):
什么是Zip 導(dǎo)入
何時在代碼中使用 Zip 導(dǎo)入
如何創(chuàng)建導(dǎo)入的ZIP文件與zipfile
如何使您的 ZIP 文件可用于從中導(dǎo)入代碼
您還將學(xué)習(xí)如何使用該zipimport模塊從 ZIP 文件中動態(tài)導(dǎo)入代碼,而無需將它們添加到 Python 的模塊搜索路徑中。為此,您將編寫一個從 ZIP 文件加載 Python 代碼的最小插件系統(tǒng)。
要從本教程中獲得最大收益,您應(yīng)該事先了解 Python 的導(dǎo)入系統(tǒng)的工作原理。您還應(yīng)該了解使用 操作 ZIP 文件zipfile、處理文件和使用with語句的基礎(chǔ)知識。
了解 Python Zip 導(dǎo)入
從 Python?2.3 開始,您可以從ZIP 文件中導(dǎo)入模塊和包。此功能稱為Zip 導(dǎo)入,當(dāng)您需要將完整包作為單個文件分發(fā)時非常有用,這是其最常見的用例。
PEP 273引入了 Zip 導(dǎo)入作為內(nèi)置功能。Python社區(qū)中的功能被廣泛接受為一個必須具備的,因為分配幾個獨(dú)立的.py,.pyc和.pyo文件并不總是恰當(dāng)?shù)暮陀行У摹?/p>
Zip 導(dǎo)入可以簡化共享和分發(fā)代碼的過程,這樣您的同事和最終用戶就不必費(fèi)力地嘗試將文件提取到正確的位置以使代碼正常工作。
注意:從Python 3.5 開始.pyo不再使用文件擴(kuò)展名。有關(guān)詳細(xì)信息,請參閱PEP 488。
PEP 302添加了一系列導(dǎo)入鉤子,為 Zip 導(dǎo)入提供內(nèi)置支持。如果您想從 ZIP 文件中導(dǎo)入模塊和包,那么您只需要該文件出現(xiàn)在 Python 的模塊搜索路徑中即可。
模塊搜索路徑是目錄和 ZIP 文件的列表。它住在sys.path.?當(dāng)您import在代碼中運(yùn)行語句時,Python 會自動搜索此列表中的項目。
在以下部分中,您將學(xué)習(xí)如何使用不同的 Python 工具和技術(shù)創(chuàng)建隨時可以導(dǎo)入的 ZIP 文件。您還將了解將這些文件添加到當(dāng)前 Python 模塊搜索路徑的幾種方法。最后,您將深入研究zipimport在幕后支持 Zip 導(dǎo)入功能的模塊。
創(chuàng)建您自己的可導(dǎo)入 ZIP 文件
Zip 導(dǎo)入允許您將跨多個模塊和包組織為單個文件的代碼快速分發(fā)。在創(chuàng)建可導(dǎo)入的 ZIP 文件時,Python 已經(jīng)涵蓋了您。標(biāo)準(zhǔn)庫中的zipfile模塊包括一個用于操作 ZIP 文件的類。它還包括一個名為 的更專業(yè)的類,它有助于創(chuàng)建可導(dǎo)入的 ZIP 文件。ZipFilePyZipFile
PyZipFile讓您可以快速有效地將 Python 代碼打包到 ZIP 文件中。該類繼承自ZipFile,因此共享相同的基接口。但是,這些類之間有兩個主要區(qū)別:
的初始值設(shè)定項PyZipFile采用名為 的可選參數(shù)optimize,它允許您通過在存檔之前將其編譯為字節(jié)碼來優(yōu)化 Python 代碼。
的PyZipFile類提供一個調(diào)用的方法.writepy(),它接受一個Python模塊或包作為參數(shù),并把它添加到一個目標(biāo)ZIP文件。
如果optimize是-1,它的默認(rèn)值,則輸入.py文件會自動編譯為.pyc文件,然后添加到目標(biāo)存檔。為什么會發(fā)生這種情況?通過跳過編譯步驟,打包.pyc文件而不是原始.py文件使導(dǎo)入過程更加高效。您將在接下來的部分中了解有關(guān)此主題的更多信息。
在以下兩節(jié)中,您將動手并開始創(chuàng)建自己的包含模塊和包的可導(dǎo)入 ZIP 文件。
將 Python 模塊捆綁到 ZIP 文件中
在本節(jié)中,您將使用PyZipFile.writepy()將.py文件編譯為字節(jié)碼并將生成的.pyc文件添加到 ZIP 存檔中。嘗試.writepy()一下,假設(shè)你有一個hello.py模塊:
"""Print a greeting message.""" # hello.py def greet(name="World"): print(f"Hello, {name}! Welcome to Real Python!")
該模塊定義了一個被調(diào)用的函數(shù)greet(),該函數(shù)接受name一個參數(shù)并將友好的問候信息打印到屏幕上。
現(xiàn)在假設(shè)您要將這個模塊打包成一個 ZIP 文件,您可以稍后導(dǎo)入。為此,您可以運(yùn)行以下代碼:
>>>
>>> import zipfile >>> with zipfile.PyZipFile("hello.zip", mode="w") as zip_module: ... zip_module.writepy("hello.py") ... >>> with zipfile.PyZipFile("hello.zip", mode="r") as zip_module: ... zip_module.printdir() ... File Name Modified Size hello.pyc 2021-10-18 05:40:04 313
運(yùn)行此代碼后,您將hello.zip在當(dāng)前工作目錄中擁有一個文件。要將呼叫.writepy()上zip_module自動編譯hello.py到hello.pyc并將其存儲在底層的ZIP文件,hello.zip。這就是.printdir()顯示hello.pyc而不是原始hello.py文件的原因。這種自動編譯可確保高效的導(dǎo)入過程。
注意:PyZipFile默認(rèn)情況下,該類不會壓縮您的 Python 模塊和包。它只是將它們存儲在一個 ZIP 文件容器中。如果你想擠進(jìn)你的源文件,您需要通過明確提供的壓縮方法compression的參數(shù)PyZipFile。目前,Python 支持Deflate、bzip2和LZMA壓縮方法。
在本教程中,你會依靠的默認(rèn)值compression,ZIP_STORED這意味著你的源文件不會被壓縮。壓縮源文件可能會影響導(dǎo)入操作的性能,您將在本教程后面了解到。
您也可以手工打包.py,并.pyc通過使用任何常規(guī)文件成ZIP文件的文件歸檔。如果生成的存檔包含.py沒有相應(yīng).pyc文件的文件,則 Python 將在您第一次從該特定 ZIP 文件導(dǎo)入時編譯它們。
Python 不會修改底層 ZIP 文件來添加新編譯的.pyc文件。因此,下次運(yùn)行導(dǎo)入時,Python 將再次編譯代碼。此行為會使導(dǎo)入過程變慢。
您還可以將目錄作為第一個參數(shù)傳遞給.writepy().?如果輸入目錄不是 Python 包,則該方法會掃描其中的.py文件,將它們編譯為.pyc文件,并將這些.pyc文件添加到目標(biāo) ZIP 文件的頂層。掃描步驟不是遞歸的,這意味著不會掃描子目錄中的源文件。
您可以通過將optimize參數(shù)設(shè)置PyZipFile為以下值之一來進(jìn)一步調(diào)整編譯過程:
使用這些值,您可以在.writepy()將.py文件編譯為.pyc文件之前微調(diào)要使用的優(yōu)化級別。
到目前為止,您已經(jīng)學(xué)習(xí)了如何將一個或多個模塊捆綁到一個 ZIP 文件中。在您的日常編碼中,您可能還需要壓縮一個完整的 Python 包。您將在下一節(jié)中了解如何執(zhí)行此操作。
將 Python 包捆綁到 ZIP 文件中
您還可以使用PyZipFile及其.writepy()方法將Python 包捆綁到 ZIP 文件中。正如您已經(jīng)了解到的,如果您將常規(guī)目錄作為第一個參數(shù)傳遞給.writepy(),則該方法會掃描目錄中的.py文件,編譯它們,并將相應(yīng)的.pyc文件添加到生成的 ZIP 文件中。
另一方面,如果輸入目錄是 Python 包,則.writepy()編譯所有.py文件并將它們添加到 ZIP 文件中,保持包的內(nèi)部結(jié)構(gòu)。
要嘗試.writepy()使用 Python 包,請創(chuàng)建一個新hello/目錄并將您的hello.py文件復(fù)制到其中。然后添加一個空__init__.py模塊,將目錄變成一個包。您應(yīng)該最終得到以下結(jié)構(gòu):
hello/ | ├── __init__.py └── hello.py
現(xiàn)在假設(shè)您想將此包捆綁到一個 ZIP 文件中以進(jìn)行分發(fā)。如果是這種情況,那么您可以運(yùn)行以下代碼:
>>>
>>> import zipfile >>> with zipfile.PyZipFile("hello_pkg.zip", mode="w") as zip_pkg: ... zip_pkg.writepy("hello") ... >>> with zipfile.PyZipFile("hello_pkg.zip", mode="r") as zip_pkg: ... zip_pkg.printdir() ... File Name Modified Size hello/__init__.pyc 2021-10-18 05:56:00 110 hello/hello.pyc 2021-10-18 05:56:00 319
調(diào)用.writepy()將hello包作為參數(shù),在其中搜索.py文件,將它們編譯為.pyc文件,最后將它們添加到目標(biāo) ZIP 文件中,保持相同的包結(jié)構(gòu)。
了解 Zip 導(dǎo)入的限制
當(dāng)您使用 ZIP 文件分發(fā) Python 代碼時,您需要考慮 Zip 導(dǎo)入的一些限制:
加載動態(tài)文件,如.pyd,.dll和.so,是不可能的。
從.py文件中導(dǎo)入代碼意味著性能上的妥協(xié)。
如果解壓庫不可用,從壓縮文件導(dǎo)入代碼將失敗。
您可以在 ZIP 存檔中包含任何類型的文件。然而,當(dāng)用戶免受這些存檔導(dǎo)入代碼,只有.py,.pyw,.pyc,和.pyo文件讀取。從動態(tài)文件,如導(dǎo)入代碼.pyd,.dll和.so,如果他們生活在一個ZIP文件是不可能的。例如,您無法從 ZIP 存檔加載用C編寫的共享庫和擴(kuò)展模塊。
您可以通過從 ZIP 文件中提取動態(tài)模塊,將它們寫入文件系統(tǒng),然后加載它們的代碼來解決此限制。但是,這意味著您需要創(chuàng)建臨時文件并處理可能的錯誤和安全風(fēng)險,這會使事情變得復(fù)雜。
正如您在本教程前面學(xué)到的那樣,Zip 導(dǎo)入也可能意味著性能妥協(xié)。如果您的存檔包含.py模塊,那么 Python 將編譯它們以滿足導(dǎo)入。但是,它不會保存相應(yīng)的.pyc文件。此行為可能會降低導(dǎo)入操作的性能。
最后,如果您需要從壓縮的 ZIP 文件中導(dǎo)入代碼,則zlib必須在您的工作環(huán)境中用于解壓。zlib如果此庫不可用,則從壓縮檔案中導(dǎo)入代碼將失敗并顯示丟失消息。此外,解壓步驟為導(dǎo)入過程增加了額外的性能開銷。由于這些原因,您將在本教程中使用未壓縮的 ZIP 文件。
從 ZIP 文件導(dǎo)入 Python 代碼
到目前為止,您已經(jīng)學(xué)習(xí)了如何創(chuàng)建自己的可導(dǎo)入 ZIP 文件以進(jìn)行分發(fā)。現(xiàn)在假設(shè)您在另一端,并且您正在獲取帶有 Python 模塊和包的 ZIP 文件。你怎么能從他們那里導(dǎo)入代碼?在本節(jié)中,您將獲得此問題的答案,并了解如何使 ZIP 文件可用于導(dǎo)入其內(nèi)容。
要讓 Python 從 ZIP 文件中導(dǎo)入代碼,該文件必須在 Python 的模塊搜索路徑中可用,該路徑存儲在 .zip 文件中sys.path。此模塊級變量包含指定模塊搜索路徑的字符串列表。內(nèi)容包括:path
包含您正在運(yùn)行的腳本的目錄
當(dāng)前目錄,如果您以交互方式運(yùn)行解釋器
PYTHONPATH環(huán)境變量中的目錄(如果已設(shè)置)
取決于您的特定 Python 安裝的目錄列表
任何路徑中列出的目錄配置文件(.pth文件)
下表指出了將 ZIP 文件添加到 的幾種方法sys.path:
在以下部分中,您將探索這三種添加項目的方法,sys.path以便您可以使 ZIP 文件可用于導(dǎo)入其內(nèi)容。
使用sys.path動態(tài)的郵編進(jìn)口
因為sys.path是一個list對象,所以您可以使用常規(guī)list方法從 Python 代碼中操作它。在一般情況下,新的項目添加到一個list對象,你可以使用.insert(),.append()或.extend()。
通常,您將使用從 Python 代碼中.insert(0, item)添加新項目sys.path。調(diào)用.insert()這種方式會item在列表的開頭插入,確保新添加的項目優(yōu)先于現(xiàn)有項目。在可能發(fā)生名稱沖突時item,使用開頭使您能夠隱藏現(xiàn)有模塊和包。
現(xiàn)在假設(shè)您需要將hello.zip包含hello.py模塊的文件添加到當(dāng)前 Python 的sys.path.?在這種情況下,您可以運(yùn)行以下示例中的代碼。請注意,要在您的計算機(jī)上運(yùn)行此示例,您需要提供正確的路徑hello.zip:
>>>
>>> import sys >>> # Insert the hello.zip into sys.path >>> sys.path.insert(0, "/path/to/hello.zip") >>> sys.path[0] '/path/to/hello.zip' >>> # Import and use the code >>> import hello >>> hello.greet("Pythonista") Hello, Pythonista! Welcome to Real Python!
將路徑添加到hello.zip后sys.path,您就可以hello.py像使用任何常規(guī)模塊一樣從中導(dǎo)入對象。
如果像 一樣hello_pkg.zip,您的 ZIP 文件包含一個 Python 包,那么您也可以將其添加到sys.path。在這種情況下,導(dǎo)入應(yīng)該是包相關(guān)的:
>>>
>>> import sys >>> sys.path.insert(0, "/path/to/hello_pkg.zip") >>> from hello import hello >>> hello.greet("Pythonista") Hello, Pythonista! Welcome to Real Python!
因為你的代碼現(xiàn)在在一個包中,你需要hello從hello包中導(dǎo)入模塊。然后您可以greet()像往常一樣訪問該功能。
添加項目的另一種選擇sys.path是使用.append().?此方法將單個對象作為參數(shù)并將其添加到基礎(chǔ)列表的末尾。重新啟動 Python 交互式會話并運(yùn)行提供以下路徑的代碼hello.zip:
>>>
>>> import sys >>> sys.path.append("/path/to/hello.zip") >>> # The hello.zip file is at the end of sys.path >>> sys.path[-1] '/path/to/hello.zip' >>> from hello import greet >>> greet("Pythonista") Hello, Pythonista! Welcome to Real Python!
這種技術(shù)的工作原理類似于使用.insert().?但是,ZIP 文件的路徑現(xiàn)在位于sys.path.?如果列表中的任何前面的項目包含名為 的模塊hello.py,則 Python 將從該模塊而不是從您新添加的hello.py模塊導(dǎo)入。
您還可以.append()在循環(huán)中使用將多個文件添加到sys.path,或者您可以只使用.extend().?此方法采用可迭代的項目并將其內(nèi)容添加到基礎(chǔ)列表的末尾。與 一樣.append(),請記住,這.extend()會將您的文件添加到 的末尾sys.path,因此現(xiàn)有名稱可以隱藏 ZIP 文件中的模塊和包。
使用PYTHONPATH全系統(tǒng)郵編進(jìn)口
在某些情況下,您可能需要一個給定的 ZIP 文件,以便從您在計算機(jī)上運(yùn)行的任何腳本或程序中導(dǎo)入其內(nèi)容。在這些情況下,您可以使用PYTHONPATH環(huán)境變量讓 Pythonsys.path在您運(yùn)行解釋器時自動加載您的存檔。
PYTHONPATH使用與PATH環(huán)境變量相同的格式,由os.pathsep.?在Unix系統(tǒng)上,例如 Linux 和 macOS,此函數(shù)返回一個冒號 (?:),而在 Windows 上,它返回一個分號 (?;)。
例如,如果您使用的是 Linux 或 macOS,則可以通過運(yùn)行以下命令將hello.zip文件添加到PYTHONPATH:
$ export PYTHONPATH="$PYTHONPATH:/path/to/hello.zip"
此命令添加/path/to/hello.zip到您的當(dāng)前PYTHONPATH并導(dǎo)出它,以便它在當(dāng)前終端會話中可用。
注意:上面的命令會導(dǎo)出一個自定義版本,PYTHONPATH其中包含hello.zip.?此自定義版本的變量僅在您當(dāng)前的命令行會話中可用,并且在您關(guān)閉會話后將丟失。
如果您將Bash作為當(dāng)前的shell運(yùn)行,那么您可以PYTHONPATH通過將以下代碼添加到您的.bashrc文件中,使此自定義版本可用于您的所有命令行會話:
# .bashrc if [ -f /path/to/hello.zip ]; then export PYTHONPATH="$PYTHONPATH:/path/to/hello.zip" fi
此代碼檢查hello.zip您的文件系統(tǒng)上是否存在。如果是,那么它將文件添加到PYTHONPATH變量中并導(dǎo)出它。因為每次啟動新的命令行實(shí)例時,Bash 都會運(yùn)行這個文件,所以自定義PYTHONPATH將在每個會話中可用。
現(xiàn)在您可以發(fā)出python命令來運(yùn)行解釋器。到了那里后,sys.path照常檢查以下內(nèi)容:
>>>
>>> import sys >>> sys.path [..., '/path/to/hello.zip', ...]
涼爽的!您的hello.zip文件在列表中。從現(xiàn)在開始,您將能夠hello.py像在上一節(jié)中所做的那樣從中導(dǎo)入對象。來試試看吧!
在上述輸出中需要注意的重要一點(diǎn)是,您的hello.zip文件不在 的開頭sys.path,這意味著hello根據(jù) Python 處理其模塊 seach path 的方式,較早出現(xiàn)的同名模塊將優(yōu)先于您的模塊。
要PYTHONPATH在 Windows 系統(tǒng)上添加項目,您可以在cmd.exe窗口中執(zhí)行命令:
C:\> set PYTHONPATH=%PYTHONPATH%;C:\path\to\hello.zip
此命令添加C:\path\to\hello.zip到PYTHONPATHWindows 計算機(jī)上變量的當(dāng)前內(nèi)容。要檢查它,請在同一個命令提示符會話中運(yùn)行 Python 解釋器,sys.path并像以前一樣查看 的內(nèi)容。
注意:同樣,PYTHONPATH您使用上述命令設(shè)置的變量將僅在您當(dāng)前的終端會話中可用。要PYTHONPATH在 Windows 上永久設(shè)置變量,請了解如何在 Windows 中添加到 PYTHONPATH
將目錄和 ZIP 文件添加到PYTHONPATH環(huán)境變量使這些條目可用于您在手頭的終端會話下運(yùn)行的任何 Python 解釋器。最后,重要的是要注意 Python 會默默地忽略 中列出的不存在的目錄和 ZIP 文件PYTHONPATH,因此請注意這一點(diǎn)。
使用.pth文件進(jìn)行解釋器范圍的 Zip 導(dǎo)入
有時,您可能只想在運(yùn)行特定的 Python 解釋器時從給定的 ZIP 文件中導(dǎo)入代碼。當(dāng)您的項目使用該 ZIP 文件中的代碼并且您不希望該代碼可用于您的其余項目時,這很有用。
Python 的路徑配置文件允許您sys.path使用自定義目錄和 ZIP 文件擴(kuò)展給定解釋器的路徑。
路徑配置文件使用.pth文件擴(kuò)展名,可以保存目錄和 ZIP 文件的路徑列表,每行一個。sys.path每次運(yùn)行提供.pth文件的 Python 解釋器時,都會添加此路徑列表。
Python 的.pth文件有一個簡單的格式:
每行必須包含一個路徑條目。
空白行和以數(shù)字符號 (?#)開頭的行將被跳過。
以 開頭的行import被執(zhí)行。
有了合適的.pth文件后,您需要將其復(fù)制到站點(diǎn)目錄之一,以便 Python 可以找到它并加載其內(nèi)容。要獲取當(dāng)前 Python 環(huán)境的站點(diǎn)目錄,您可以getusersitepackages()從site模塊調(diào)用。如果您在當(dāng)前機(jī)器上沒有管理員權(quán)限,那么您可以使用用戶站點(diǎn)目錄site.USER_SITE.
注意:您的主文件夾中可能不存在用戶站點(diǎn)目錄。如果這是您的情況,那么請按照所需的路徑結(jié)構(gòu)隨意創(chuàng)建它。
例如,以下命令hello.pth在 Ubuntu 上為系統(tǒng)范圍的 Python 3 解釋器創(chuàng)建一個路徑配置文件:
$ sudo nano /usr/lib/python3/dist-packages/hello.pth
此命令hello.pth使用GNU nano文本編輯器創(chuàng)建root.?在那里,輸入hello.zip文件的路徑。按Ctrl+X,然后Y,最后保存文件Enter。現(xiàn)在,sys.path當(dāng)您再次啟動系統(tǒng) Python 解釋器時,此 ZIP 文件將可用:
>>>
>>> import sys >>> sys.path [..., '/path/to/hello.zip', ...]
而已!從現(xiàn)在開始,hello.py只要您使用系統(tǒng)范圍的 Python 解釋器,就可以從中導(dǎo)入對象。
同樣,sys.path當(dāng) Python 讀取和加載給定.pth文件的內(nèi)容時,不會添加不存在的目錄和 ZIP文件。最后,.pth文件中的重復(fù)條目僅添加到sys.path.
探索 Python?zipimport:Zip 導(dǎo)入背后的工具
您已經(jīng)在zipimport不知不覺中使用了標(biāo)準(zhǔn)庫中的模塊。在幕后,當(dāng)sys.path項目包含 ZIP 文件的路徑時,Python 的內(nèi)置導(dǎo)入機(jī)制會自動使用此模塊。在本節(jié)中,您將zipimport通過一個實(shí)際示例了解其工作原理以及如何在代碼中顯式使用它。
了解基礎(chǔ)知識?zipimport
的主要成分zipimport是zipimporter。此類將 ZIP 文件的路徑作為參數(shù)并創(chuàng)建導(dǎo)入器實(shí)例。下面是一個如何使用的例子zipimporter以及它的一些屬性和方法:
>>>
>>> from zipimport import zipimporter >>> importer = zipimporter("/path/to/hello.zip") >>> importer.is_package("hello") False >>> importer.get_filename("hello") '/path/to/hello.zip/hello.pyc' >>> hello = importer.load_module("hello") >>> hello.__file__ '/path/to/hello.zip/hello.pyc' >>> hello.greet("Pythonista") Hello, Pythonista! Welcome to Real Python!
在本例中,您首先zipimporter從zipimport.?然后zipimporter使用hello.zip文件路徑創(chuàng)建一個實(shí)例。
本zipimporter類提供了一些有用的屬性和方法。例如,如果輸入名稱是包,則.is_package()返回True,F(xiàn)alse否則返回。該.get_filename()方法返回.__file__存檔內(nèi)給定模塊的路徑 (?)。
如果要將模塊的名稱帶入當(dāng)前命名空間,則可以使用.load_module(),它返回對輸入模塊的引用。通過該引用,您可以像往常一樣訪問模塊中的任何代碼對象
構(gòu)建插件系統(tǒng)?zipimport
正如您在上面了解到的,Python 在內(nèi)部使用zipimport從 ZIP 文件加載代碼。您還了解到該模塊提供了可在某些實(shí)際編碼情況下使用的工具。例如,假設(shè)您要實(shí)現(xiàn)一個自定義插件系統(tǒng),其中每個插件都位于自己的 ZIP 文件中。您的代碼應(yīng)該在給定文件夾中搜索 ZIP 文件并自動導(dǎo)入插件的功能。
要實(shí)際體驗這個示例,您將實(shí)現(xiàn)兩個玩具插件,它們接收一條消息和一個標(biāo)題,并將它們顯示在您的默認(rèn) Web 瀏覽器和Tkinter消息框中。每個插件都應(yīng)該位于自己的目錄中,在一個名為plugin.py.?這個模塊應(yīng)該實(shí)現(xiàn)插件的功能并提供一個main()函數(shù)作為插件的入口點(diǎn)。
繼續(xù)并創(chuàng)建一個文件夾,web_message/其中包含一個plugin.py文件。在您喜歡的代碼編輯器或 IDE 中打開該文件,然后為 Web 瀏覽器插件鍵入以下代碼:
"""A plugin that displays a message using webbrowser.""" # web_message/plugin.py import tempfile import webbrowser def main(text, title="Alert"): with tempfile.NamedTemporaryFile( mode="w", suffix=".html", delete=False ) as home: html = f"""
{text}
""" home.write(html) path = "file://" + home.name webbrowser.open(path)main()此代碼中的函數(shù)接受一條text消息和一個窗口title。然后它NamedTemporaryFile在with語句中創(chuàng)建一個。該文件將包含一個最小的HTML文檔顯示title,并text在網(wǎng)頁上。要在默認(rèn) Web 瀏覽器中打開此文件,請使用webbrowser.open().
下一個插件提供了類似的功能,但使用了Tkinter工具包。這個插件的代碼也應(yīng)該存在于一個名為plugin.py.?您可以將模塊放在tk_message/文件系統(tǒng)中調(diào)用的目錄中:
"""A plugin that displays a message using Tkinter.""" # tk_message/plugin.py import tkinter from tkinter import messagebox def main(text, title="Alert"): root = tkinter.Tk() root.withdraw() messagebox.showinfo(title, text)
遵循與 Web 瀏覽器插件相同的模式,main()將text和title.?在這種情況下,該函數(shù)會創(chuàng)建一個Tk實(shí)例來保存插件的頂級窗口。但是,您不需要顯示該窗口,只需要顯示一個消息框。因此,您可以使用.withdraw()隱藏根窗口,然后調(diào)用.showinfo()上messagebox顯示與輸入的對話text和title。
現(xiàn)在您需要將每個插件打包成自己的 ZIP 文件。為此,請在包含和文件夾的目錄中啟動 Python交互式會話并運(yùn)行以下代碼:web_message/tk_message/
>>>
>>> import zipfile >>> plugins = ("web_message", "tk_message") >>> for plugin in plugins: ... with zipfile.PyZipFile(f"{plugin}.zip", mode="w") as zip_plugin: ... zip_plugin.writepy(plugin) ...
下一步是為您的插件系統(tǒng)創(chuàng)建一個根文件夾。此文件夾必須包含一個plugins/目錄,其中包含新創(chuàng)建的 ZIP 文件。以下是您的目錄的外觀:
rp_plugins/ │ ├── plugins/ │ │ │ ├── tk_message.zip │ └── web_message.zip │ └── main.py
在 中main.py,您將放置插件系統(tǒng)的客戶端代碼。繼續(xù)并填充main.py以下代碼:
1# main.py 2 3import zipimport 4from pathlib import Path 5 6def load_plugins(path): 7 plugins = [] 8 for zip_plugin in path.glob("*.zip"): 9 importer = zipimport.zipimporter(zip_plugin) 10 plugin_module = importer.load_module("plugin") 11 plugins.append(getattr(plugin_module, "main")) 12 return plugins 13 14if __name__ == "__main__": 15 path = Path("plugins/") 16 plugins = load_plugins(path) 17 for plugin in plugins: 18 plugin("Hello, World!", "Greeting!")
以下是此代碼的逐行工作方式:
第 3 行導(dǎo)入zipimport以從相應(yīng)的 ZIP 文件動態(tài)加載您的插件。
第 4 行導(dǎo)入pathlib以管理系統(tǒng)路徑。
第 6 行定義了load_plugins(),它采用包含插件檔案的目錄的路徑。
第 7 行創(chuàng)建一個空列表來保存當(dāng)前插件。
第 8 行定義了一個for循環(huán),它遍歷.zipplugins 目錄中的文件。
第 9 行為zipimporter系統(tǒng)中的每個插件創(chuàng)建一個實(shí)例。
第 10 行plugin從每個插件 ZIP 文件加載模塊。
第 11行將每個插件的main()功能附加到plugins列表中。
12號線?返回的plugins列表給調(diào)用者。
第 14 到 18 行調(diào)用load_plugins()生成可用插件的當(dāng)前列表并在循環(huán)中執(zhí)行它們。
如果您從命令行運(yùn)行main.py腳本,那么您首先會看到一個 Tkinter 消息框,顯示Hello, World!消息和Greeting!標(biāo)題。關(guān)閉該窗口后,您的 Web 瀏覽器將在新頁面上顯示相同的消息和標(biāo)題。來試試看吧!
結(jié)論
如果 ZIP 文件在模塊搜索路徑中可用,Python 可以直接從 ZIP 文件導(dǎo)入代碼。此功能稱為Zip 導(dǎo)入。您可以利用 Zip 導(dǎo)入將模塊和包捆綁到單個存檔中,以便您可以快速有效地將它們分發(fā)給最終用戶。
如果您經(jīng)常將 Python 代碼捆綁到 ZIP 文件中并且需要在日常任務(wù)中使用該代碼,您還可以利用 Zip 導(dǎo)入。
在本教程中,您學(xué)習(xí)了:
什么是Zip 導(dǎo)入
何時以及如何使用 Zip 導(dǎo)入
如何構(gòu)建導(dǎo)入的ZIP文件與zipfile
如何使 ZIP 文件可用于導(dǎo)入機(jī)制
您還編寫了一個動手示例,說明如何使用zipimport.?通過這個示例,您學(xué)習(xí)了如何在 Python 中從 ZIP 文件中動態(tài)導(dǎo)入代碼。
Python 網(wǎng)絡(luò)
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。