Python 模組介紹,以及建立和匯入 Python 模組
由於套件是一種特殊的模組,本節所講述的內容也適用於 Python 套件。要想深入了解模組的相關概念,你可以檢視Python 指南的Python 模組完整名稱,模組快取介紹一節。
Python 模組
你可以簡單的將 Python 腳本檔案視為模組,雖然這並非得到 Python 模組的唯一方式,但卻是最為常見的。Python 模組包含了一系列程式碼,他們可能是變數,函式或類別的定義,雖然沒有強製性的約束,但同一模組中的程式碼關聯性較高。比如,模組math
擁有大量用於數學運算的函式。
如何命名 Python 模組?
在預設情況下,Python 模組的名稱等同於其對應的py
腳本檔案的檔案名,並不需要特意的命名。比如,檔案worker.py
對應的模組的名稱為woker
。
Python 模組的儲存形式
在專案的開發過程中,Python 模組以py
腳本檔案的形式出現,如果經過了編譯,模組也可以儲存在pyc
位元組碼檔案中。
下面是一個簡單的模組hello
,他使用print
函式顯示了一段資訊。
# 模組 hello
print('這裏是模組 hello!')
Python 模組的特性
在 Python 腳本檔案中,你可以定義變數,函式,類別等內容,這些內容即為模組的特性(Attribute),他們通過形式為m.name
,m.name=value
的陳述式被存取,其中m
為模組,name
為特性(名稱,識別碼),value
為特性的新值。
當然,模組特性可以被稱呼為變數,函式或類別,這並沒有什麽問題。
如何檢視 Python 模組的所有特性?
Python 模組擁有的特殊特性__dict__
,包含了模組所有特性的資訊。__dict__
本身是一個字典,字典的鍵是表示特性名稱的字串,字典的值是特性的具體內容。
對模組特性的存取,等同於對__dict__
鍵值組的存取,假設模組student
擁有特性count
,那麽,student.count=100
等同於student.__dict__['count']=100
。
如何為 Python 模組新增暫時特性?
在沒有限製的情況下,你可以為一個 Python 模組新增暫時特性,只需要使用正常的指派陳述式m.name=value
即可。其中m
為模組,name
為特性(名稱,識別碼),value
為特性的值。
如何判斷 Python 模組是否為程式進入點?
模組的__name__
特性表示模組的名稱,該特性可在程式碼中修改。當一個模組被當作程式進入點時,其模組名稱將被設定為'__main__'
,因此,可通過判斷__name__
是否等於'__main__'
,來執行模組作為程式進入點時的特定程式碼。
如何取得 Python 模組對應的檔案路徑?
模組的__file__
特性是 Python 模組對應的檔案路徑,如果該模組是通過檔案載入的。當模組通過非檔案方式載入時,那麽他可能不具備__file__
特性,此時對__file__
進行讀取將導致例外狀況AttributeError
。
# 判斷 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 模組的私用特性
Python 模組有沒有私用特性,是模棱兩可的,你可以為 Python 特性指定以一個或更多底線(_
)開頭的名稱,以表示該特性不適合被外部存取。但事實上,Python 並不會阻止此類情況的發生,通過模組存取以底線開頭的特性是被允許的。
Python 模組中以底線(_
)開頭的特性,可能不會被from…import
陳述式匯入,這一點會在稍後給予說明。
Python 模組的文件字串
與 Python 函式類似,模組也具有文件字串,他被儲存在 Python 模組的__doc__
特性中,以向開發人員說明模組的相關資訊。文件字串本質上是書寫在模組中的字串常值,他必須是模組的第一行有效程式碼,可以采用緊密相鄰或加入空白字元的方式進行拼接,但不應包含需要運算的內容或操作。
在模組teacher
中,我們拼接了兩個字串常值作為模組的文件字串。
'這裏是模組 '"teacher"
# …
建立 Python 模組
當你建立一個py
腳本時,也就建立了一個 Python 模組,這是最為簡單和常見的方式。除此之外,你還可以通過ModuleType
類別來動態的建立模組。
模組teacher
定了自己的變數count
,level
和__avg_age
,以及函式show
,_add_avg_age
。
# …
# 模組 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 模組
要在一個模組中使用另一個 Python 模組或模組的某個特性,必須先進行匯入操作,通常要用到import
或from…import
陳述式。這兩種陳述式的用法不同,但均會將目標繫結至目前的命名空間,其對應的識別碼預設為被繫結目標的名稱。
被匯入的 Python 模組將作為父級套件的特性
需要特別指出,當一個 Python 模組被成功匯入後,除了可能被繫結至目前命名空間,還將成為其父級套件的一個特性(如果父級套件存在的話),特性的名稱預設為模組名稱。因此,如果可以在另一個命名空間中存取父級套件,並且相關快取沒有失效,那麽通過父級套件存取作為其特性的 Python 模組將是可行的,即便你未在該命名空間中使用任何陳述式明確的匯入該 Python 模組。
在函式或方法中匯入的 Python 模組或特性無法被外部直接存取
由於擁有自己的命名空間,通過import
或from…import
陳述式,在函式或方法中匯入的模組和特性,將通過識別碼繫結至函式或方法的命名空間。基於有效範圍的一般性規則,這些識別碼不能在函式或方法外部使用,除非在外部命名空間中重新繫結他們。
命名空間,有效範圍
要深入了解命名空間,你可以檢視程式設計教學的命名空間,有效範圍介紹一節。
使用 import 陳述式匯入 Python 模組
import
陳述式的基本格式如下,當你使用,
時,相當於執行多個import
陳述式,比如,import sys,os
相當於執行了import sys
和import os
。
import <module>[ as <identifier>][, …]
- module 部分
module
為需要匯入模組的完整名稱,當完整名稱包含多個模組時,這些模組將被依次匯入。- identifier 部分
identifier
是用於繫結操作的自訂識別碼。
如何參考 import 陳述式匯入的 Python 模組?
雖然import
陳述式會依次匯入完整名稱中的所有模組,但在沒有as
關鍵字的情況下,被繫結至目前命名空間的是第一個模組,而非最後一個。比如,在使用import school.teacher
匯入teacher
後,依然需要通過school.teacher
來使用teacher
模組,因為命名空間中繫結的是識別碼為school
的school
模組,識別碼teacher
並不存在。
當import
陳述式包含as
關鍵字時,完整名稱中的最後一個模組將繫結至目前命名空間,並采用as
指定的識別碼。
在書寫陳述式import school.teacher as t
之後,你可以通過t
來使用模組teacher
,因為識別碼t
將於模組teacher
繫結。
以_
開頭的模組特性並沒有存取上的限製,通過模組teacher
使用變數__avg_age
和函式_add_avg_age
不會有任何問題。
# 使用 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
後,可在函式外部通過父級套件school
的teacher
特性存取teacher
模組,而不需要明確的匯入。
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_info
和float_info
特性。
form <module> import <target>[ as <identifier>][, …]
- module 部分
module
是一個完整名稱或相對名稱(以.
開頭),用於尋找需要匯入的目標所在的模組或父級套件,這些模組或套件將被依次匯入。- target 部分
target
是開發人員真正希望匯入的目標,他可以指模組,也可以指模組的特性。- identifier 部分
identifier
是用於繫結操作的自訂識別碼。
如何參考 from…import 陳述式匯入的 Python 模組或特性?
在from…import
陳述式中,關鍵字import
所指示的匯入目標,將被繫結至目前的命名空間,因此,他並不會像import
陳述式一樣讓你產生疑惑。比如,執行from school.teacher import show
後,你可以直接書寫show()
來呼叫teacher
模組的show
函式。
如何使用 from…import 陳述式匯入 Python 模組的所有特性?
在from…import
陳述式的import
關鍵字後跟隨*
,即可匯入相關 Python 模組的所有特性,這些特性將通過自己的名稱繫結至目前命名空間。此做法僅被允許出現在模組層級,函式或方法使用*
將導致錯誤。
上述中所謂的“所有”並不包含以一個或更多_
開頭的(私用)特性,如果需要,你可以在from…import
陳述式中單獨的明確的匯入他們。比如,from school.teacher import __avg_age
明確的匯入了teacher
模組的__avg_age
特性。
不能指派修改 from…import 陳述式匯入的 Python 模組變數
一旦使用from…import
陳述式匯入了 Python 模組中的變數,就可以讀取該變數,但無法直接修改他,如果你試圖對其進行指派,將等同於在目前位置定義一個新的變數。
在下面的程式碼中,我們匯入了teacher
模組的所有特性,並在目前命名空間中繫結為count
,level
和show
。陳述式count=100
並不會對teacher
模組的count
變數進行指派,而是會定義新的變數count
。
此外,由於*
並不匯入 Python 模組中以_
開頭的特性,因此,存取teacher
模組的__avg_age
特性將導致例外狀況NameError
。
# 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 陳述式匯入目前 Python 模組的父級套件?
在from…import
陳述式的target
部分中采用相對路徑(以.
開頭),即可匯入目前 Python 模組的父級套件,如果書寫..
,則將匯入父級套件以及父級套件的父級套件。在有些情況下,是否書寫.
均可匯入同一目錄中的其他模組,但由於書寫.
會額外的嘗試匯入父級套件,因此這可能導致例外狀況ImportError
的發生,當模組沒有相關父級套件時。
需要指出,在 Python 套件對應的__init__.py
檔案的from…import
陳述式中,相對路徑.
並不表示該套件的父級套件,而是表示該套件自身。
在模組student
中,我們使用.
匯入了同樣位於school
套件的guard
模組,以及teacher
模組的特性。
# 模組 student 將通過父級套件匯入其他模組
import school.student
# 以相對路徑匯入 teacher 模組的特性
from .teacher import *
print('在 student 模組中呼叫 show')
show()
# 以相對路徑匯入 guard 模組
from . import guard
print(f'守衛人員是 {guard.name}')
在 student 模組中呼叫 show
教師的數量為 0
守衛人員是 汪汪大隊
# 模組變數 name
name = '汪汪大隊'
使用 from…import * 陳述式不能匯入 Python 套件中的所有模組
使用from…import *
不能匯入 Python 套件中的所有模組,*
僅針對套件的特性。如果在執行from…import *
陳述式之前,Python 套件的一些子模組已經被匯入,那麽已匯入的子模組將被from…import *
繫結至目前命名空間(因為他們是 Python 套件的特性),此時可以直接書寫子模組名稱來使用子模組。
由於之前已經匯入了teacher
模組,因此該模組是套件school
的特性之一,from…import
陳述式會將其繫結至目前命名空間,書寫teacher
沒有任何問題。
另一個子模組student
並非套件school
的特性,因此from…import
陳述式不會將student
繫結至目前命名空間。
# 由於 teacher 模組之前已經被匯入,因此他是套件 school 的特性
from school import *
# 這裏可以使用 teacher
print(teacher)
# ERROR 存取 school 的子模組 student 會導致例外狀況
print(student)
<module 'school.teacher' from '…'>
…
NameError: name 'student' is not defined
程式碼
hello.py·codebeatme/python·GitHub
import.py·codebeatme/python·GitHub
school/teacher.py·codebeatme/python·GitHub
import_attr.py·codebeatme/python·GitHub
import_from.py·codebeatme/python·GitHub
import_parent.py·codebeatme/python·GitHub
school/student.py·codebeatme/python·GitHub
school/guard.py·codebeatme/python·GitHub