URLhttps://learnscript.net/zh-hant/python/modules/
    複製連結移至說明  範例

    Python 模組介紹,以及建立和匯入模組

    閱讀 17:03·字數 5117·更新 
    Youtube 頻道
    訂閱 375

    由於套件是一種特殊的模組,本節所講述的內容也適用於 Python 套件。要想深入了解模組的相關概念,你可以檢視Python 指南Python 模組完整名稱,模組快取介紹一節。

    Python 模組

    你可以簡單的將 Python 腳本檔案視為模組,雖然這並非得到模組的唯一方式,但卻是最為常見的。模組包含了一系列程式碼,他們可能是變數,函式或類別的定義,雖然沒有強製性的約束,但同一模組中的程式碼關聯性較高。比如,模組math擁有大量用於數學運算的函式。

    如何命名模組?

    在預設情況下,模組的名稱等同於其對應的py腳本檔案的檔案名,並不需要特意的命名。比如,檔案worker.py對應的模組的名稱為woker

    模組的儲存形式

    在專案的開發過程中,模組以py腳本檔案的形式出現,如果經過了編譯,模組也可以儲存在pyc位元組碼檔案中。

    下面是一個簡單的模組hello,他使用print函式顯示了一段資訊。

    hello.py
    # 模組 hello
    print('這裏是模組 hello!')

    Python 模組的特性

    在 Python 腳本檔案中,你可以定義變數,函式,類別等內容,這些內容即為模組的特性(Attribute),他們通過形式為m.namem.name=value的陳述式被存取,其中m為模組,name為特性(名稱,識別碼),value為特性的新值。

    當然,模組特性可以被稱呼為變數,函式或類別,這並沒有什麽問題。

    如何檢視模組的所有特性?

    模組擁有的特殊特性__dict__,包含了模組所有特性的資訊。__dict__本身是一個字典,字典的鍵是表示特性名稱的字串,字典的值是特性的具體內容。

    對模組特性的存取,等同於對__dict__鍵值組的存取,假設模組student擁有特性count,那麽,student.count=100等同於student.__dict__['count']=100

    如何為模組新增暫時特性?

    在沒有限製的情況下,你可以為一個模組新增暫時特性,只需要使用正常的指派陳述式m.name=value即可。其中m為模組,name為特性(名稱,識別碼),value為特性的值。

    如何判斷模組是否為程式進入點?

    模組的__name__特性表示模組的名稱,該特性可在程式碼中修改。當一個模組被當作程式進入點時,其模組名稱將被設定為'__main__',因此,可通過判斷__name__是否等於'__main__',來執行模組作為程式進入點時的特定程式碼。

    如何取得模組對應的檔案路徑?

    模組的__file__特性是模組對應的檔案路徑,如果該模組是通過檔案載入的。當模組通過非檔案方式載入時,那麽他可能不具備__file__特性,此時對__file__進行讀取將導致例外狀況AttributeError

    import.py
    # 判斷 import.py 是否為程式進入點
    if __name__ == '__main__':
    	# 顯示模組的檔案路徑
    	print(__file__)
    
    # 匯入模組 teacher import school.teacher # 通過 __dict__ 讀取 teacher 的 count 特性 print(school.teacher.__dict__['count'])
    # Windows 中的輸出結果
    \modules\import.py
    0

    Python 模組的私用特性

    模組有沒有私用特性,是模棱兩可的,你可以為特性指定以一個或更多底線(_)開頭的名稱,以表示該特性不適合被外部存取。但事實上,並不會封鎖此類情況的發生,通過模組存取以底線開頭的特性是被允許的。

    模組中以底線(_)開頭的特性,可能不會被from…import陳述式匯入,這一點會在稍後給予說明。

    Python 模組的文件字串

    與函式類似,模組也具有文件字串,他被儲存在模組的__doc__特性中,以向開發人員說明模組的相關資訊。文件字串本質上是書寫在模組中的字串常值,他必須是模組的第一行有效程式碼,可以采用緊密相鄰或加入空白字元的方式進行拼接,但不應包含需要運算的內容或操作。

    在模組teacher中,我們拼接了兩個字串常值作為模組的文件字串。

    school/teacher.py
    '這裏是模組 '"teacher"
    # …

    建立 Python 模組

    當你建立一個py腳本時,也就建立了一個模組,這是最為簡單和常見的方式。除此之外,你還可以通過ModuleType類別來動態的建立模組。

    模組teacher定了自己的變數countlevel__avg_age,以及函式show_add_avg_age

    school/teacher.py
    # …
    # 模組 teacher 中的變數 count,level
    count = 0
    level = 1
    
    # 模組 teacher 中的函式 show def show(): print(f'教師的數量為 {count}')
    # 使用 _ 開頭,表示不適合外部存取 __avg_age = 33 def _add_avg_age(years): # 修改模組變數 __avg_age global __avg_age __avg_age += years

    匯入 Python 模組

    要在一個模組中使用另一個模組或模組的某個特性,必須先進行匯入操作,通常要用到importfrom…import陳述式。這兩種陳述式的用法不同,但均會將目標繫結至目前的命名空間,其對應的識別碼預設為被繫結目標的名稱。

    被匯入的模組將作為父級套件的特性

    需要特別指出,當一個模組被成功匯入後,除了可能被繫結至目前命名空間,還將成為其父級套件的一個特性(如果父級套件存在的話),特性的名稱預設為模組名稱。因此,如果可以在另一個命名空間中存取父級套件,並且相關快取沒有失效,那麽通過父級套件存取作為其特性的模組將是可行的,即便你未在該命名空間中使用任何陳述式明確的匯入該模組。

    在函式或方法中匯入的模組或特性無法被外部直接存取

    由於擁有自己的命名空間,通過importfrom…import陳述式,在函式或方法中匯入的模組和特性,將通過識別碼繫結至函式或方法的命名空間。基於有效範圍的一般性規則,這些識別碼不能在函式或方法外部使用,除非在外部命名空間中重新繫結他們。

    命名空間,有效範圍

    要深入了解命名空間,你可以檢視程式設計教學命名空間,有效範圍介紹一節。

    使用 import 陳述式匯入 Python 模組

    import陳述式的基本格式如下,當你使用,時,相當於執行多個import陳述式,比如,import sys,os相當於執行了import sysimport os

    import <module>[ as <identifier>][, …]

    module 部分

    module為需要匯入模組的完整名稱,當完整名稱包含多個模組時,這些模組將被依次匯入。

    identifier 部分

    identifier是用於繫結操作的自訂識別碼。

    如何參考 import 陳述式匯入的模組?

    雖然import陳述式會依次匯入完整名稱中的所有模組,但在沒有as關鍵字的情況下,被繫結至目前命名空間的是第一個模組,而非最後一個。比如,在使用import school.teacher匯入teacher後,依然需要通過school.teacher來使用teacher模組,因為命名空間中繫結的是識別碼為schoolschool模組,識別碼teacher並不存在。

    import陳述式包含as關鍵字時,完整名稱中的最後一個模組將繫結至目前命名空間,並采用as指定的識別碼。

    在書寫陳述式import school.teacher as t之後,你可以通過t來使用模組teacher,因為識別碼t將於模組teacher繫結。

    _開頭的模組特性並沒有存取上的限製,通過模組teacher使用變數__avg_age和函式_add_avg_age不會有任何問題。

    import.py
    # 使用 as 關鍵字指定識別碼
    import school.teacher as t
    
    # 可以通過 t 來使用 teacher 模組 t.count = 100 t.show()
    # 通過模組呼叫所謂的私用特性 t._add_avg_age(1) print(f'現在的平均年齡是 {t.__avg_age}')
    教師的數量為 100
    現在的平均年齡是 34

    下面的程式碼,在函式中匯入了模組teacher,該模組將成為父級套件school的一個特性,特性名稱為teacher,因此,在呼叫函式show_teacher後,可在函式外部通過父級套件schoolteacher特性存取teacher模組,而不需要明確的匯入。

    import_attr.py
    import school
    
    def show_teacher(): # 識別碼 school 繫結在函式的命名空間中 import school.teacher # 通過識別碼 school 存取模組 teacher 的 show 函式 school.teacher.show()
    # 呼叫函式後,teacher 模組將成為 school 的一個特性 show_teacher() # 通過 school 的特性存取模組 teacher print(f'Teacher 模組?{school.teacher}')
    教師的數量為 0
    Teacher 模組?<module 'school.teacher' from ''>

    使用 from…import 陳述式匯入 Python 模組

    import陳述式不同,from…import陳述式可以使用模組的相對名稱(以.開頭)或完整名稱,並且匯入的目標不僅限於模組,還包括模組的特性,其語法格式如下。當你使用,時可以匯入多個目標,比如,from sys import int_info,float_info,分別匯入了sys模組的int_infofloat_info特性。

    form <module> import <target>[ as <identifier>][, …]

    module 部分

    module是一個完整名稱或相對名稱(以.開頭),用於尋找需要匯入的目標所在的模組或父級套件,這些模組或套件將被依次匯入。

    target 部分

    target是開發人員真正希望匯入的目標,他可以指模組,也可以指模組的特性。

    identifier 部分

    identifier是用於繫結操作的自訂識別碼。

    如何參考 from…import 陳述式匯入的模組或特性?

    from…import陳述式中,關鍵字import所指示的匯入目標,將被繫結至目前的命名空間,因此,他並不會像import陳述式一樣讓你產生疑惑。比如,執行from school.teacher import show後,你可以直接書寫show()來呼叫teacher模組的show函式。

    如何使用 from…import 陳述式匯入模組的所有特性?

    from…import陳述式的import關鍵字後跟隨*,即可匯入相關模組的所有特性,這些特性將通過自己的名稱繫結至目前命名空間。此做法僅被允許出現在模組層級,函式或方法使用*將導致錯誤。

    上述中所謂的「所有」並不包含以一個或更多_開頭的(私用)特性,如果需要,你可以在from…import陳述式中單獨的明確的匯入他們。比如,from school.teacher import __avg_age明確的匯入了teacher模組的__avg_age特性。

    不能指派修改 from…import 陳述式匯入的模組變數

    一旦使用from…import陳述式匯入了模組中的變數,就可以讀取該變數,但無法直接修改他,如果你試圖對其進行指派,將等同於在目前位置定義一個新的變數。

    在下面的程式碼中,我們匯入了teacher模組的所有特性,並在目前命名空間中繫結為countlevelshow。陳述式count=100並不會對teacher模組的count變數進行指派,而是會定義新的變數count

    此外,由於*並不匯入模組中以_開頭的特性,因此,存取teacher模組的__avg_age特性將導致例外狀況NameError

    import_from.py
    # teacher 模組的特性 count,level,show 將被繫結至目前命名空間
    from school.teacher import *
    
    # 此處的 count 指向 teacher 模組的 count 特性 print(f'我在模組 modules 中讀取的教師數量為 {count}') show()
    # 這裏會定義新的變數 count,而不是對 teacher 中的 count 進行指派 count = 100 # 呼叫 show 後,並不會顯示 100 show()
    # ERROR * 並不會匯入私用特性 print(__avg_age)
    我在模組 modules 中讀取的教師數量為 0
    教師的數量為 0
    教師的數量為 0

    NameError: name '__avg_age' is not defined

    如何使用 from…import 陳述式匯入目前模組的父級套件?

    from…import陳述式的target部分中采用相對路徑(以.開頭),即可匯入目前模組的父級套件,如果書寫..,則將匯入父級套件以及父級套件的父級套件。在有些情況下,是否書寫.均可匯入同一目錄中的其他模組,但由於書寫.會額外的嘗試匯入父級套件,因此這可能導致例外狀況ImportError的發生,當模組沒有相關父級套件時。

    需要指出,在套件對應的__init__.py檔案的from…import陳述式中,相對路徑.並不表示該套件的父級套件,而是表示該套件自身。

    在模組student中,我們使用.匯入了同樣位於school套件的guard模組,以及teacher模組的特性。

    import_parent.py
    # 模組 student 將通過父級套件匯入其他模組
    import school.student
    school/student.py
    # 以相對路徑匯入 teacher 模組的特性
    from .teacher import *
    
    print('在 student 模組中呼叫 show') show()
    # 以相對路徑匯入 guard 模組 from . import guard print(f'守衛人員是 {guard.name}')
    在 student 模組中呼叫 show
    教師的數量為 0
    守衛人員是 汪汪大隊
    school/guard.py
    # 模組變數 name
    name = '汪汪大隊'

    使用 from…import * 陳述式不能匯入套件中的所有模組

    使用from…import *不能匯入套件中的所有模組,*僅針對套件的特性。如果在執行from…import *陳述式之前,套件的一些子模組已經被匯入,那麽已匯入的子模組將被from…import *繫結至目前命名空間(因為他們是套件的特性),此時可以直接書寫子模組名稱來使用子模組。

    由於之前已經匯入了teacher模組,因此該模組是套件school的特性之一,from…import陳述式會將其繫結至目前命名空間,書寫teacher沒有任何問題。

    另一個子模組student並非套件school的特性,因此from…import陳述式不會將student繫結至目前命名空間。

    import_from.py
    # 由於 teacher 模組之前已經被匯入,因此他是套件 school 的特性
    from school import *
    
    # 這裏可以使用 teacher print(teacher) # ERROR 存取 school 的子模組 student 會導致例外狀況 print(student)
    <module 'school.teacher' from ''>

    NameError: name 'student' is not defined

    原始碼

    src/zh-hant/modules·codebeatme/python·GitHub