這次我們繼續探險,來搞定 python 中的模塊(module)。**兵馬未動,糧草先行**,開工之前先看看基礎是否補齊了。 ...
目錄
- 前言
- 基礎
- 什麼是 python 中的模塊?
- 引入模塊有幾種方式?
- 模塊的查找順序
- 模塊中包含執行語句的情況
- 用 dir() 函數來窺探模塊
- python 的內置模塊有哪些?
- 結語
- 參考文檔
- 系列文章列表
前言
這次我們繼續探險,來搞定 python 中的模塊(module)。兵馬未動,糧草先行,開工之前先看看基礎是否補齊了^_^。
基礎
模塊的概念你一定不會陌生吧,這是一個非常寬泛的概念,在各行各業都會用到。這裡我們涉及的只是軟體中的模塊概念。說到模塊,就得先瞭解下模塊化程式設計的概念。(如果您對模塊化程式設計的概念已經爛熟於心,盡可以略過,而直接跳到下一節)
模塊化程式設計
模塊化程式設計是指進行程式設計時將一個大程式按照功能劃分為若幹小程式模塊,每個小程式模塊完成一個確定的功能,並且在這些模塊之間建立必要的聯繫,通過模塊的相互協作完成整個功能的程式設計方法。
——百度百科
舉個例子,假如我們要造一輛汽車,就可以先將汽車分成四個部件:發動機、底盤、電氣設備、車身。每個部件都是一個模塊,都會完成自己專屬的功能,同時也預留了和其他部件配合的介面。當這幾部分建造完成時,就可以組合在一起,從而完成了整個汽車的建造。
類比一下,汽車就是整個程式,而像發動機,底盤等就是程式中各個小的模塊。只有當各個模塊正常工作,並且暴露的介面和其他模塊的介面完全契合,才能組合成一個完整且正常工作的程式。
模塊化有哪些好處?
當然,如果不將程式分解成一個個獨立的部分,而是整個一大坨,也能夠完成所要的功能。那麼為什麼教科書還有實際使用中都會提倡模塊化程式設計?這樣做有什麼好處呢?
控製程序設計的複雜度
不知你看過《代碼大全》沒有,裡面有一句非常著名的格言:軟體的首要使命就是管理複雜度。完整的軟體功能複雜度是非常高的,如果不使用有效的方法加以管理,很可能會陷入複雜的泥潭中不可自拔。而將程式分解成模塊,則會將整體功能的複雜度有效的下分到各個模塊中。每個模塊只要能夠管理好自己的複雜度就可以了。
提高代碼的重用性
還是以造汽車為例,假設我造了一個很牛的發動機,多款車型都可以使用它。程式設計也一樣,如果一個模塊能夠完成特定的功能,且與父程式耦合度較小,多個程式都可以使用它。
易於維護和擴展
小 A 寫了一個程式,並將各個部分劃分的非常明確,再加以人性化的函數命名和註釋。即使有一天小 A 離職了,小 B 要接過來維護以及在此基礎上再開發新的功能也不難。
既然模塊化就這麼多好處, 強大的 python 當然也會吸收這個優秀的設計思想,並且在語言中有所體現,那就是 python 的模塊(module)。
什麼是 python 中的模塊?
先來看一個示例:
創建 python 文件 a.py,併在文件中定義函數
sum
def sum(a, b): return a + b
創建 python 文件 b.py, 並調用
sum
函數from a import sum print(sum(1, 2)) # 3
文件 a.py
就是一個模塊(module),b.py
就是一個主模塊(main module)。
在 b.py
中有這麼一句 from a import sum
,是指將模塊 a 中的 sum
函數導入到當前模塊中。我們定義的文件名是 a.py ,而模塊名就是去掉尾碼後得到的 模塊 a。那麼能不能再多導入幾個函數或者導入模塊 a 的全部函數呢?當然可以,這個我們後面講。
調用模塊時,通過文件名就可以確定模塊的名字,那麼在模塊(module)內部,能知道自己姓甚名誰嗎?還真能。
每個模塊都有一個全局變數 __name__
,它就是模塊的名字。上面 a.py 的內容不變,修改下 b.py
的內容。
import a
print(a.__name__) # a
print(a.sum(1, 2)) # 3
來,一起總結下:
- python 模塊(module) 是指包含 python 定義(包括 類,函數,變數)和語句的文件(.py做尾碼)
- 模塊名就是模塊文件名稱去掉.py 尾碼
- 在模塊內部,可以通過全局變數
__name__
得到模塊名稱
引入模塊有幾種方式?
要導入模塊並調用,前提要導入的 python 模塊中有料(函數,變數,class)才可以。先來定義一個 python 模塊 calc
def plus(a, b):
return a + b
def subtract(a, b):
return a - b
再創建一個 main.py 文件,在其中做引入操作。okay,準備好了,那我們來逐個看下可以引入模塊的方式吧。
引入整個模塊,調用時需要加上模塊名
import calc print(calc.plus(1, 2)) # 3 print(calc.subtract(2, 1)) # 1
引入模塊特定的函數或變數,調用時無需加模塊名
from calc import plus, subtract print(plus(1, 2)) # 3 print(subtract(2, 1)) # 1
引入整個模塊,調用時無需加上模塊名
from calc import * print(plus(1, 2)) # 3 print(subtract(2, 1)) # 1
引入整個模塊,並對模塊重命名,調用時加上重命名後的模塊名
import calc as calculator print(calculator.plus(1, 2)) # 3 print(calculator.subtract(2, 1)) # 1
引入模塊特定的函數或變數,並對其重命名,調用時無需加模塊名
from calc import plus as add, subtract as sub print(add(1, 2)) # 3 print(sub(2, 1)) # 1
數一下,一共是 6 種方式,歸納一下就是
from
,import
,as
,*
這些符號的組合而已。
模塊的查找順序
在上幾篇文章中已經用瞭如 os,shutils,json 等多個模塊 ,這些模塊都是 python 的內置模塊。相比之下,我們剛纔使用的 calc
模塊就是自定義模塊。
假設我們使用 import calc
導入 calc
模塊, python 在啟動時按照什麼樣的順序來查找這個模塊呢?
- 先查找內置(built-in)模塊中有沒有,如果沒有轉到 2
- 查找
sys.path
變數指定的路徑下有沒有, 有的話就使用,沒有就報錯
sys.path
變數中存儲了那些路徑呢?
當前運行的 python 腳本所在的目錄
環境變數
PYTHONPATH
中的路徑,它和 shell 環境變數PATH
差不多這個變數可以使用 python 腳本在運行時修改它
預設的 python 安裝包的路徑
想要看下你的電腦當前 sys.path
有哪些路徑嗎?運行下麵代碼就可以
import sys
print(sys.path)
查找模塊的順序是從前向後,只要查到就使用,因此這個變數存儲路徑的順序很重要。
模塊中包含執行語句的情況
如果引入的模塊中包含一些執行語句,那麼在導入模塊時這些語句就會執行。但是即使同樣的模塊被導入了兩次,這些語句也只能執行一次。
來看下麵的例子, 定義 calc
模塊
print('I am clac module')
def plus(a, b):
return a + b
def subtract(a, b):
return a - b
並且在 main.py 中定義導入兩次 calc
模塊的函數
from calc import plus
from calc import subtract
print(plus(1, 2))
print(subtract(1, 2))
結果是 'I am clac module'
只會被列印一次。
用 dir() 函數來窺探模塊
dir()
函數是 python 的內置函數,可用來獲取模塊的屬性,方法等信息,當我們剛接觸一個模塊,不清楚它由哪些有用的屬性和方法時,就可以用 dir()
來一探究竟。
以常用的 json模塊 為例,我們來展示下它的屬性和方法
import json
print(dir(json))
# ['JSONDecodeError', 'JSONDecoder', 'JSONEncoder', '__all__', '__author__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', '__version__', '_default_decoder', '_default_encoder', 'codecs', 'decoder', 'detect_encoding', 'dump', 'dumps', 'encoder', 'load', 'loads', 'scanner']
其中以雙下劃線開頭的變數,如 __name__
並非是模塊自己定義的,而是與模塊相關的預設屬性。
如果我想查看當前模塊內的所有屬性和方法呢?去掉 dir()
函數的參數就可以。拿上節的代碼為例來看下。
from calc import plus
from calc import subtract
print(plus(1, 2))
print(subtract(1, 2))
print(dir())
# ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'plus', 'subtract']
我們會看到 calc 模塊的 plus
和 substract
方法也展示了出來,那麼 dir
函數究竟是從哪裡獲取的數據,背後的機理是什麼呢?
其實每個模塊內部都有一個子集的私有符號表,它就是模塊內所有函數和方法共用的全局符號表。當模塊 B 導入模塊 A 時,就會把要導入的模塊 A 或者特定的方法,屬性放置到模塊 B 的全局符號表中,dir()
函數也就是從模塊中的全局符號表中獲取出的值。
python 的內置模塊有哪些?
python 的內置模塊太豐富了,幾乎可以滿足我們日常的任何需求。既然有輪子就在那裡,而且這輪子又快又好,又何必再造輪子呢?快來看下它的常用內置模塊有哪些。
模塊名 | 功能簡述 |
---|---|
calendar | 與日期相關 |
datetime | 處理日期和時間 |
csv | 讀寫 csv 文件 |
json | 讀寫 json 格式數據 |
collections | 提供有用的數據結構 |
io | 處理 I/O 流 |
os | 基本的操作系統函數訪問 |
shutil | 高級文件處理 |
tempfile | 創建臨時文件和目錄 |
logging | 日誌功能 |
random | 生成偽隨機數 |
copy | 複製數據相關 |
codec | 編解碼 |
re | 正則表達式 |
uuid | 全局唯一標識符 (UUID) |
multiprocessing | 運行多個子進程 |
threading | 線程 |
concurrent | 非同步 |
argparse | 解析命令行參數 |
atexit | 註冊在程式退出時調用的函數 |
signal | 處理 POSIX 信號 |
光常用的模塊就這麼一大堆,確實是很難都記住,記不住也沒關係,當需要用到的時候隨用隨查就可以了。
結語
本篇中主要介紹了模塊化的定義,引入模塊化的方式,模塊的查找順序,常用的內置模塊簡介等內容,通過使用模塊化,能夠更好的實踐模塊化程式設計的思想。但本篇並沒有涉及 package 的概念,會在後續章節講述。
下篇會講述 python 中正則表達式,敬請期待。
參考文檔
系列文章列表
- python 歷險記(一)—String,集合(List,元組,Dict)
- python 歷險記(二)— python 的面向對象
- python 歷險記(三)— python 的常用文件操作
- python 歷險記(四)— python 中常用的 json 操作