6. 模組(Module) — Python 3.10.6 說明文件
文章推薦指數: 80 %
Import 套件時,Python 會搜尋 sys.path 裡的目錄,尋找套件的子目錄。
目錄中必須含有 __init__.py 檔案,才會被Pyhon 當成套件;這樣可以避免一些以常用名稱 ...
瀏覽
索引
模組|
下一頁|
上一頁|
Python»
3.10.6Documentation»
Python教學»
6.模組(Module)
|
6.模組(Module)¶
如果從Python直譯器離開後又再次進入,之前(幫函式或變數)做的定義都會消失。
因此,想要寫一些比較長的程式時,你最好使用編輯器來準備要輸入給直譯器的內容,並且用該檔案來運行它。
這就是一個腳本(script)。
隨著你的程式越變越長,你可能會想要把它分開成幾個檔案,讓它比較好維護。
你可能也會想用一個你之前已經在其他程式寫好的函式,但不想要複製該函式的原始定義到所有使用它的程式裡。
為了支援這一點,Python有一種方法可以將定義放入檔案中,並在互動模式下的直譯器中使用它們。
這種檔案稱為模組(module);模組中的定義可以被import到其他模組中,或是被import至主(main)模組(在最頂層執行的腳本,以及互動模式下,所使用的變數集合)。
模組是指包含Python定義和語句的檔案,檔案名稱是模組名稱加上.py。
在模組中,模組的名稱(作為字串)會是全域變數__name__的值。
例如,用您喜歡的文字編輯器在資料夾中創一個名為fibo.py的檔案,內容如下:
#Fibonaccinumbersmodule
deffib(n):#writeFibonacciseriesupton
a,b=0,1
whilea
使用此模組名稱,就可以存取函式:
>>>fibo.fib(1000)
01123581321345589144233377610987
>>>fibo.fib2(100)
[0,1,1,2,3,5,8,13,21,34,55,89]
>>>fibo.__name__
'fibo'
如果您打算經常使用其中某個函式,可以將其指定至區域變數:
>>>fib=fibo.fib
>>>fib(500)
01123581321345589144233377
6.1.深入了解模組¶
模組可以包含可執行的陳述式以及函式的定義。
這些陳述式是作為模組的初始化,它們只會在第一次被import時才會執行。
1(如果檔案被當成腳本執行,也會執行它們)。
每個模組都有它自己的私有命名空間(namespace),模組內定義的函式會把該模組的私有符號表當成全域命名空間使用。
因此,模組的作者可以在模組中使用全域變數,而不必擔心和使用者的全域變數發生意外的名稱衝突。
另一方面,如果你知道自己在做什麼,你可以用這個方式取用模組的全域變數,以和引用函式一樣的寫法,modname.itemname。
在一個模組中可以import其他模組。
把所有的import陳述式放在模組(就這邊來說,腳本也是一樣)的最開頭是個慣例,但並沒有強制。
如放置在模組的最高層(不在任何函式或class中),被import的模組名稱將被加入全域命名空間中。
import陳述式有另一種變形寫法,可以直接將名稱從欲import的模組,直接import至原模組的命名空間中。
例如:
>>>fromfiboimportfib,fib2
>>>fib(500)
01123581321345589144233377
在import之後的名稱會被導入,但定義該函式的整個模組名稱並不會被引入在區域命名空間中(因此,示例中的fibo未被定義)。
甚至還有另一種變形寫法,可以import模組定義的所有名稱:
>>>fromfiboimport*
>>>fib(500)
01123581321345589144233377
這個寫法會import模組中所有的名稱,除了使用底線(_)開頭的名稱。
大多數情況下,Python程式設計師不大使用這個功能,因為它在直譯器中引入了一組未知的名稱,並且可能覆蓋了某些您已經定義的內容。
請注意,一般情況下並不建議從模組或套件中import*的做法,因為它通常會導致可讀性較差的程式碼。
但若是使用它來在互動模式中節省打字時間,則是可以接受的。
如果模組名稱後面出現as,則as之後的名稱將直接和被import模組綁定在一起。
>>>importfiboasfib
>>>fib.fib(500)
01123581321345589144233377
這個import方式和importfibo實質上是一樣的,唯一的差別是現在要用fib使用模組。
在使用from時也可以用同樣的方式獲得類似的效果:
>>>fromfiboimportfibasfibonacci
>>>fibonacci(500)
01123581321345589144233377
備註
出於效率原因,每個模組在每個直譯器session中僅會被import一次。
因此,如果您更改了模組,則必須重啟直譯器——或者,如果只是一個想要在互動模式下測試的模組,可以使用importlib.reload()。
例如:importimportlib;importlib.reload(modulename)。
6.1.1.把模組當作腳本執行¶
當使用以下內容運行Python模組時:
pythonfibo.py
這意味著,透過在模組的末尾添加以下程式碼:
if__name__=="__main__":
importsys
fib(int(sys.argv[1]))
你可以將檔案作為腳本也同時可以作為被import的模組,因為剖析(parse)命令列的程式碼只會在當模組是「主」檔案時,才會執行:
$pythonfibo.py50
0112358132134
如果此模組是被import的,則該段程式碼不會被執行:
>>>importfibo
>>>
這通常是用來為模組提供方便的使用者介面,或者用於測試目的(執行測試套件時,以腳本的方式執行模組)。
6.1.2.模組的搜尋路徑¶
Import一個名為spam的模組時,直譯器首先會搜尋具有該名稱的內建模組。
模組名稱列在sys.builtin_module_names當中。
如果找不到,接下來會在變數sys.path所給定的資料夾清單之中,搜尋一個名為spam.py的檔案。
sys.path從這些位置開始進行初始化:
輸入腳本所位在的資料夾(如未指定檔案時,則是當前資料夾)。
PYTHONPATH(一連串和shell變數PATH的語法相同的資料夾名稱)。
與安裝相關的預設值(按慣例會包含一個site-packages資料夾,它是由site模組所處理)。
備註
在支援符號連結(symlink)的檔案系統中,輸入腳本的所在資料夾是在跟隨符號連結之後才被計算的。
換言之,包含符號連結的資料夾並沒有增加到模組的搜尋路徑中。
初始化之後,Python程式可以修改sys.path。
執行中腳本的所在資料夾會在搜尋路徑的開頭,在標準函式庫路徑之前。
這代表該資料夾中的腳本會優先被載入,而不是函式庫資料夾中相同名稱的模組。
除非是有意要做這樣的替換,否則這是一個錯誤。
請參見標準模組以瞭解更多資訊。
6.1.3.「編譯」Python檔案¶
為了加快載入模組的速度,Python將每個模組的編譯版本暫存在__pycache__資料夾下,並命名為module.version.pyc,這裡的version是編譯後的檔案的格式名稱,且名稱通常會包含Python的版本編號。
例如,在CPython3.3中,spam.py的編譯版本將被暫存為__pycache__/spam.cpython-33.pyc。
此命名準則可以讓來自不同版本的編譯模組和Python的不同版本同時共存。
Python根據原始碼最後修改的日期,檢查編譯版本是否過期而需要重新編譯。
這是一個完全自動的過程。
另外,編譯後的模組獨立於平台,因此不同架構的作業系統之間可以共用同一函式庫。
Python在兩種情況下不檢查快取(cache)。
首先,它總是重新編譯且不儲存直接從命令列載入的模組的結果。
第二,如果沒有源模組,則不會檢查快取。
要支援非源模組(僅編譯)的發布,編譯後的模組必須位於原始資料夾中,並且不能有源模組。
一些給專家的秘訣:
可以在Python指令上使用開關參數(switch)-O或-OO來減小已編譯模組的大小。
開關參數-O刪除assert(斷言)陳述式,而-OO同時刪除assert陳述式和__doc__字串。
由於有些程式可能依賴於上述這些內容,因此只有在您知道自己在做什麼時,才應使用此參數。
「已優化」模組有opt-標記,且通常較小。
未來的版本可能會改變優化的效果。
讀取.pyc檔案時,程式的執行速度並不會比讀取.py檔案快。
唯一比較快的地方是載入的速度。
模組compileall可以為資料夾中的所有模組創建.pyc檔。
更多的細節,包括決策流程圖,請參考PEP3147。
6.2.標準模組¶
Python附帶了一個標準模組庫,詳細的介紹在另一份文件,稱為「Python函式庫參考手冊」(簡稱為「函式庫參考手冊」)。
有些模組是直譯器中內建的;它們使一些不屬於語言核心但依然內建的運算得以存取,其目的是為了提高效率,或提供作業系統基本操作(例如系統呼叫)。
這些模組的集合是一個組態選項,它們取決於底層平台。
例如:winreg模組僅供Windows使用。
值得注意的模組是sys,它被內建在每個Python直譯器中。
變數sys.ps1和sys.ps2則用來定義主、次提示字元的字串:
>>>importsys
>>>sys.ps1
'>>>'
>>>sys.ps2
'...'
>>>sys.ps1='C>'
C>print('Yuck!')
Yuck!
C>
只有直譯器在互動模式時,才需要定義這兩個變數。
變數sys.path是一個字串list,它決定直譯器的模組搜尋路徑。
它的初始值為環境變數PYTHONPATH中提取的預設路徑,或是當PYTHONPATH未設定時,從內建預設值提取。
你可以用標準的list操作修改該變數:
>>>importsys
>>>sys.path.append('/ufs/guido/lib/python')
6.3.dir()函式¶
內建函式dir()用於找出模組定義的所有名稱。
它回傳一個排序後的字串list:
>>>importfibo,sys
>>>dir(fibo)
['__name__','fib','fib2']
>>>dir(sys)
['__breakpointhook__','__displayhook__','__doc__','__excepthook__',
'__interactivehook__','__loader__','__name__','__package__','__spec__',
'__stderr__','__stdin__','__stdout__','__unraisablehook__',
'_clear_type_cache','_current_frames','_debugmallocstats','_framework',
'_getframe','_git','_home','_xoptions','abiflags','addaudithook',
'api_version','argv','audit','base_exec_prefix','base_prefix',
'breakpointhook','builtin_module_names','byteorder','call_tracing',
'callstats','copyright','displayhook','dont_write_bytecode','exc_info',
'excepthook','exec_prefix','executable','exit','flags','float_info',
'float_repr_style','get_asyncgen_hooks','get_coroutine_origin_tracking_depth',
'getallocatedblocks','getdefaultencoding','getdlopenflags',
'getfilesystemencodeerrors','getfilesystemencoding','getprofile',
'getrecursionlimit','getrefcount','getsizeof','getswitchinterval',
'gettrace','hash_info','hexversion','implementation','int_info',
'intern','is_finalizing','last_traceback','last_type','last_value',
'maxsize','maxunicode','meta_path','modules','path','path_hooks',
'path_importer_cache','platform','prefix','ps1','ps2','pycache_prefix',
'set_asyncgen_hooks','set_coroutine_origin_tracking_depth','setdlopenflags',
'setprofile','setrecursionlimit','setswitchinterval','settrace','stderr',
'stdin','stdout','thread_info','unraisablehook','version','version_info',
'warnoptions']
沒有給引數時,dir()列出目前已定義的名稱:
>>>a=[1,2,3,4,5]
>>>importfibo
>>>fib=fibo.fib
>>>dir()
['__builtins__','__name__','a','fib','fibo','sys']
請注意,它列出所有類型的名稱:變數、模組、函式等。
dir()不會列出內建函式和變數的名稱。
如果你想要列出它們,它們被定義在標準模組builtins內:
>>>importbuiltins
>>>dir(builtins)
['ArithmeticError','AssertionError','AttributeError','BaseException',
'BlockingIOError','BrokenPipeError','BufferError','BytesWarning',
'ChildProcessError','ConnectionAbortedError','ConnectionError',
'ConnectionRefusedError','ConnectionResetError','DeprecationWarning',
'EOFError','Ellipsis','EnvironmentError','Exception','False',
'FileExistsError','FileNotFoundError','FloatingPointError',
'FutureWarning','GeneratorExit','IOError','ImportError',
'ImportWarning','IndentationError','IndexError','InterruptedError',
'IsADirectoryError','KeyError','KeyboardInterrupt','LookupError',
'MemoryError','NameError','None','NotADirectoryError','NotImplemented',
'NotImplementedError','OSError','OverflowError',
'PendingDeprecationWarning','PermissionError','ProcessLookupError',
'ReferenceError','ResourceWarning','RuntimeError','RuntimeWarning',
'StopIteration','SyntaxError','SyntaxWarning','SystemError',
'SystemExit','TabError','TimeoutError','True','TypeError',
'UnboundLocalError','UnicodeDecodeError','UnicodeEncodeError',
'UnicodeError','UnicodeTranslateError','UnicodeWarning','UserWarning',
'ValueError','Warning','ZeroDivisionError','_','__build_class__',
'__debug__','__doc__','__import__','__name__','__package__','abs',
'all','any','ascii','bin','bool','bytearray','bytes','callable',
'chr','classmethod','compile','complex','copyright','credits',
'delattr','dict','dir','divmod','enumerate','eval','exec','exit',
'filter','float','format','frozenset','getattr','globals','hasattr',
'hash','help','hex','id','input','int','isinstance','issubclass',
'iter','len','license','list','locals','map','max','memoryview',
'min','next','object','oct','open','ord','pow','print','property',
'quit','range','repr','reversed','round','set','setattr','slice',
'sorted','staticmethod','str','sum','super','tuple','type','vars',
'zip']
6.4.套件(Package)¶
套件是一種使用「點分隔模組名稱」組織Python模組命名空間的方法。
例如,模組名稱A.B表示套件A中名為B的子模組。
正如模組使用時,不同模組的作者不需擔心與其他模組的全域變數名稱重複,點分隔模組名稱的使用,也讓多模組套件(像NumPy或Pillow)的作者們不須擔心其他套件的模組名稱。
假設你想設計一個能統一處理音訊檔案及音訊數據的模組集(「套件」)。
因為音訊檔案有很多的不同的格式(通常以它們的副檔名來辨識,例如:.wav,.aiff,.au),因此,為了不同檔案格式之間的轉換,你會需要建立和維護一個不斷增長的模組集合。
為了要達成對音訊數據的許多不同作業(例如,音訊混合、增加回聲、套用等化器功能、創造人工立體音效),你將編寫一系列無止盡的模組來執行這些作業。
以下是你的套件可能的架構(以階層式檔案系統的方式表示):
sound/Top-levelpackage
__init__.pyInitializethesoundpackage
formats/Subpackageforfileformatconversions
__init__.py
wavread.py
wavwrite.py
aiffread.py
aiffwrite.py
auread.py
auwrite.py
...
effects/Subpackageforsoundeffects
__init__.py
echo.py
surround.py
reverse.py
...
filters/Subpackageforfilters
__init__.py
equalizer.py
vocoder.py
karaoke.py
...
Import套件時,Python會搜尋sys.path裡的目錄,尋找套件的子目錄。
目錄中必須含有__init__.py檔案,才會被Pyhon當成套件;這樣可以避免一些以常用名稱命名(例如string)的目錄,無意中隱藏了較晚出現在模組搜尋路徑中的有效模組。
在最簡單的情況,__init__.py可以只是一個空白檔案;但它也可以執行套件的初始化程式碼,或設置__all__變數,之後會詳述。
套件使用者可以從套件中import個別模組,例如:
importsound.effects.echo
這樣就載入了子模組sound.effects.echo。
引用時必須用它的全名:
sound.effects.echo.echofilter(input,output,delay=0.7,atten=4)
另一種import子模組的方法是:
fromsound.effectsimportecho
這段程式碼一樣可以載入子模組echo,並且不加套件前綴也可以使用,因此能以如下方式使用:
echo.echofilter(input,output,delay=0.7,atten=4)
另一種變化是直接import所需的函式或變數:
fromsound.effects.echoimportechofilter
同樣地,這樣也會載入子模組echo,但它的函式echofilter()就可以直接使用:
echofilter(input,output,delay=0.7,atten=4)
請注意,使用frompackageimportitem時,item可以是套件的子模組(或子套件),也可以是套件中被定義的名稱,像是函式、class(類別)或變數。
import陳述式首先測試套件中有沒有定義該item;如果沒有,則會假設它是模組,並嘗試載入。
如果還是找不到item,則會引發ImportError例外。
相反地,使用importitem.subitem.subsubitem語法時,除了最後一項之外,每一項都必須是套件;最後一項可以是模組或套件,但不能是前一項中定義的class、函式或變數。
6.4.1.從套件中import*¶
當使用者寫下fromsound.effectsimport*時,會發生什麼事?理想情況下,我們可能希望程式碼會去檔案系統,尋找套件中存在的子模組,並將它們全部import。
這會花費較長的時間,且import子模組的過程可能會有不必要的副作用,這些副作用只有在明確地import子模組時才會發生。
唯一的解法是由套件作者為套件提供明確的索引。
import陳述式使用以下慣例:如果套件的__init__.py程式碼有定義一個名為__all__的list,若遇到frompackageimport*的時候,它就會是要被import的模組名稱。
發布套件的新版本時,套件作者可自行決定是否更新此list。
如果套件作者認為沒有人會從他的套件中import*,他也可能會決定不支援這個list。
舉例來說,sound/effects/__init__.py檔案可包含以下程式碼:
__all__=["echo","surround","reverse"]
意思是,fromsound.effectsimport*將會importsound.effects套件中,這三個被提名的子模組。
如果__all__沒有被定義,fromsound.effectsimport*陳述式並不會把sound.effects套件中所有子模組都import到當前的命名空間;它只保證sound.effects套件有被import(可能會運行__init__.py中的初始化程式碼),然後import套件中被定義的全部名稱。
這包含__init__.py定義(以及被明確載入的子模組)的任何名稱。
它也包括任何之前被import陳述式明確載入的套件子模組。
請看以下程式碼:
importsound.effects.echo
importsound.effects.surround
fromsound.effectsimport*
此例中,當from...import陳述式被執行時,echo和surround模組被import進當前的命名空間,因為它們是在sound.effects套件裡定義的。
(當__all__有被定義時,這規則也有效。
)
雖然,有些特定模組的設計,讓你使用import*時,該模組只會輸出遵循特定樣式的名稱,但在正式環境(production)的程式碼中這仍然被視為是不良習慣。
記住,使用frompackageimportspecific_submodule不會有任何問題!實際上,這是推薦用法,除非import的模組需要用到的子模組和其他套件的子模組同名。
6.4.2.套件內引用¶
當套件的結構為多個子套件的組合時(如同範例中的sound套件),可以使用「絕對(absolute)import」,引用同層套件中的子模組。
例如,要在sound.filters.vocoder模組中使用sound.effects中的echo模組時,可以用fromsound.effectsimportecho。
你也可以用frommoduleimportname的import陳述式,編寫「相對(relative)import」。
這些import使用前導句號指示相對import中的當前套件和母套件。
例如,在surround模組中,你可以使用:
from.importecho
from..importformats
from..filtersimportequalizer
請注意,相對import的運作是以目前的模組名稱為依據。
因為主模組的名稱永遠是"__main__",所以如果一個模組預期被用作Python應用程式的主模組,那它必須永遠使用絕對import。
6.4.3.多目錄中的套件¶
套件也支援一個特殊屬性__path__。
它在初始化時是一個list,包含該套件的__init__.py檔案所在的目錄名稱,初始化時機是在這個檔案的程式碼被執行之前。
這個變數可以被修改,但這樣做會影響將來對套件內的模組和子套件的搜尋。
雖然這個特色不太常被需要,但它可用於擴充套件中的模組集合。
註解
1
實際上,函式定義也是「被執行」的「陳述式」;在執行模組階層的函式定義時,會將函式名稱加到模組的全域命名空間。
目录
6.模組(Module)
6.1.深入了解模組
6.1.1.把模組當作腳本執行
6.1.2.模組的搜尋路徑
6.1.3.「編譯」Python檔案
6.2.標準模組
6.3.dir()函式
6.4.套件(Package)
6.4.1.從套件中import*
6.4.2.套件內引用
6.4.3.多目錄中的套件
上個主題
5.資料結構
下個主題
7.輸入和輸出
此頁面
回報錯誤
顯示原始碼
瀏覽
索引
模組|
下一頁|
上一頁|
Python»
3.10.6Documentation»
Python教學»
6.模組(Module)
|
延伸文章資訊
- 16. 模組(Module) — Python 3.10.6 說明文件
Import 套件時,Python 會搜尋 sys.path 裡的目錄,尋找套件的子目錄。 目錄中必須含有 __init__.py 檔案,才會被Pyhon 當成套件;這樣可以避免一些以常用名稱 ...
- 2Day05 - Python 常用模組-技術 - 拾貝文庫網
Day05 - Python 常用模組. ... 模組簡介. 模組就是一個儲存了Python 程式碼的檔案。模組能定義函式,類和變數。模組裡也能包含可執行的程式碼。 模組也是Python 物件,...
- 3解析Python模組(Module)和套件(Package)的概念
模組(Module)就是一個檔案,包含了相關性較高的程式碼。隨著應用程式的開發規模越來越大,我們不可能把所有的程式碼都寫在同一份Python檔案中 ...
- 4第6 章Python 模組
一個模組是一個檔案,內含Python 程式的指令,可讓其他程式應用. ▸ Python 標準函式庫(Standard library) 內含許多模組. ✶ 只要匯入模組,就可以 ... (3) ...
- 5Django筆記- Python的模組與套件 - dokelung's Blog
章節簡介模組與套件模組與匯入名稱空間(namespace) 匯入到頂層空間不安全的匯入套件第三方程式庫PyPI Python的套件管理程式-pip 常用套件簡...