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

閱讀 18:09·字數 5448·更新 
Youtube 頻道
訂閱 133

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

Python 模組

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

如何命名 Python 模組?

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

Python 模組的儲存形式

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

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

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

Python 模組的特性

在 Python 腳本檔案中,你可以定義變數,函式,類別等內容,這些內容即為模組的特性(Attribute),他們通過形式為m.namem.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
# 判斷 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中,我們拼接了兩個字串常值作為模組的文件字串。

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

建立 Python 模組

當你建立一個py腳本時,也就建立了一個 Python 模組,這是最為簡單和常見的方式。除此之外,你還可以通過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 模組

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

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

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

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

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

命名空間,有效範圍

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

使用 import 陳述式匯入 Python 模組

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

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

module 部分

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

identifier 部分

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

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

雖然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 陳述式匯入的 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模組的所有特性,並在目前命名空間中繫結為countlevelshow。陳述式count=100並不會對teacher模組的count變數進行指派,而是會定義新的變數count

此外,由於*並不匯入 Python 模組中以_開頭的特性,因此,存取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 陳述式匯入目前 Python 模組的父級套件?

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

需要指出,在 Python 套件對應的__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 * 陳述式不能匯入 Python 套件中的所有模組

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

由於之前已經匯入了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

程式碼

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