Python 套件,常規套件,命名空間套件介紹
本節所講述的內容,並不完全適用於通過類別ModuleType
動態建立的表示套件的模組型別物件。
Python 套件
Python 套件是一類特殊的模組,他並不對應同名的py
腳本檔案,而是對應同名的資料夾。
如何判斷 Python 模組是否為套件?
在程式碼層面,如果一個模組具有__path__
特性,則該模組為 Python 套件。__path__
特性可用於指示 Python 套件對應的資料夾路徑。
在 Python 的互動模式中,我們嘗試檢視模組re
和os
的__path__
特性。從輸出結果可以看出,模組re
是套件,模組os
不是套件。
import re
re.__path__
['…\\python312.zip\\re']
import os
os.__path__
…
AttributeError: module 'os' has no attribute '__path__'. Did you mean: '__all__'?
import re
re.__path__
['/usr/lib/python3.11/re']
import os
os.__path__
…
AttributeError: module 'os' has no attribute '__path__'. Did you mean: '__all__'?
如何命名 Python 套件?
Python 套件的名稱預設與其對應的資料夾名稱相同,並不需要特意的命名。比如,一個名稱為workers
的資料夾,其對應的套件名稱為workers
。
應該稱呼 Python 模組還是 Python 套件?
由於套件是一種特殊的模組,因此,稱呼某個套件為模組並不算錯誤,比如,re
模組。
模組
如果你需要了解 Python 模組,可以檢視Python 模組完整名稱,模組快取介紹一節。
Python 常規套件
Python 常規套件對應的資料夾,擁有一個名為__init__.py
的檔案,他相當於模組對應的同名py
檔案,包含套件的相關程式碼。
Python 常規套件的儲存形式
在編寫程式碼期間,Python 常規套件儲存為資料夾和資料夾中__init__.py
檔案。但編譯後,常規套件的相關程式碼會轉變為位元組碼並儲存在pyc
檔案中。
這裏,套件hero
對應的資料夾包含__init__.py
,因此hero
是一個常規套件。
# hero 是一個常規套件
print('我是常規套件 hero!')
Python 命名空間套件
如果套件對應的資料夾未包含名為__init__.py
的檔案,則該套件被稱為命名空間套件。
命名空間套件和常規套件之間的區別
事實上,常規套件和命名空間套件都具有命名空間的效果,在不同的常規套件或命名空間套件中,你可以定義名稱相同的內容。至於區別,除了是否包含檔案__init__.py
,還有以下幾點。
命名空間套件的__file__
特性為None
,而常規套件為__init__.py
檔案的路徑。命名空間套件的__path__
特性為_NamespacePath
型別,而常規套件為串列。命名空間套件的__loader__
特性為NamespaceLoader
型別,而常規套件為SourceFileLoader
型別。
Python 模組的 __loader__ 特性
Python 模組的__loader__
特性表示用於載入該模組的載入器。
這裏,我們新增一個空的資料夾enemies
,他將對應命名空間套件。切換命令列至enemies
的上級資料夾,啟動 Python 互動模式並使用import
進行匯入操作,然後檢視enemies
套件的相關資訊。
import enemies
enemies.__file__ == None
True
enemies.__path__
_NamespacePath(['…\\packages\\enemies'])
enemies.__loader__
<_frozen_importlib_external.NamespaceLoader object at…>
import enemies
enemies.__file__ == None
True
enemies.__path__
_NamespacePath(['…/packages/enemies'])
enemies.__loader__
<_frozen_importlib_external.NamespaceLoader object at…>
命名空間,有效範圍
要了解什麽是命名空間,可以檢視程式設計教學的命名空間,有效範圍介紹一節。
zip 檔案中的 Python 套件
在zip
檔案中,Python 套件只能以常規套件的形式存在,他們應該具有腳本檔案__init__.py
,否則將無法被匯入。
在下面的範例中,壓縮檔案plants.zip
含有常規套件flowers
,與plants.zip
處於同一目錄的腳本檔案my_plants.py
,會嘗試匯入zip
中的flowers
套件。啟動命令列並切換至檔案my_plants.py
所在的目錄,執行後可以看到相關的輸出結果。
# flowers 是一個常規套件
print('我是套件 flowers!')
# 取得壓縮檔案 plants.zip 的絕對路徑,並新增至模組搜尋路徑
import os
import sys
zip_path = os.path.abspath('plants.zip')
sys.path.append(zip_path)
# 匯入 plants.zip 中的 flowers 套件
import flowers
我是套件 flowers!
如果將plants.zip
中的腳本檔案__init__.py
刪除,那麽重複上述執行步驟會引發例外狀況ModuleNotFoundError
。
Python 子模組和子套件
Python 套件對應的資料夾中的py
檔案或子資料夾,即為該套件的子模組和子套件,子套件可以是常規套件,也可以是命名空間套件。
Python 模組和套件的執行優先順序
雖然這並不符合規格,但處於同一位置的 Python 模組和套件可以具有相同的名稱。在這種情況下,匯入操作將按照如下優先順序進行,常規套件的優先順序最高,模組次之,命名空間套件的優先順序最低。當同名模組或套件中的一個被匯入後,另一個將被忽略。
在套件school
中,存在子套件homework
和子模組homework
,其中子套件homework
是一個常規套件。在腳本檔案my_school.py
(與school
套件對應的資料夾處於同一目錄)中,運算式import school.homework
匯入的是常規套件homework
,而不是模組homework
。
print('功課太多了!')
print('這裏是模組 homework!')
# 匯入套件 school 的子套件 homework,他是一個常規套件
import school.homework
功課太多了!
如果把資料夾homework
中的腳本檔案__init__.py
刪除,那麽常規套件homework
會變為命名空間套件,再次執行my_school.py
將匯入模組homework
。
這裏是模組 homework!
內容分類
程式碼
hero/__init__.py·codebeatme/python-reference·GitHub
plants/flowers/__init__.py·codebeatme/python-reference·GitHub
my_plants.py·codebeatme/python-reference·GitHub
school/homework/__init__.py·codebeatme/python-reference·GitHub
school/homework.py·codebeatme/python-reference·GitHub
my_school.py·codebeatme/python-reference·GitHub