python 歷險記(五)— python 中的模塊

前言

此次咱們繼續探險,來搞定 python 中的模塊(module)。兵馬未動,糧草先行,開工以前先看看基礎是否補齊了^_^。python

基礎

模塊的概念你必定不會陌生吧,這是一個很是寬泛的概念,在各行各業都會用到。這裏咱們涉及的只是軟件中的模塊概念。說到模塊,就得先了解下模塊化程序設計的概念。(若是您對模塊化程序設計的概念已經爛熟於心,盡能夠略過,而直接跳到下一節)正則表達式

模塊化程序設計

模塊化程序設計是指進行程序設計時將一個大程序按照功能劃分爲若干小程序模塊,每一個小程序模塊完成一個肯定的功能,而且在這些模塊之間創建必要的聯繫,經過模塊的相互協做完成整個功能的程序設計方法。shell

——百度百科json

舉個例子,假如咱們要造一輛汽車,就能夠先將汽車分紅四個部件:發動機、底盤、電氣設備、車身。每一個部件都是一個模塊,都會完成本身專屬的功能,同時也預留了和其餘部件配合的接口。當這幾部分建造完成時,就能夠組合在一塊兒,從而完成了整個汽車的建造。小程序

類比一下,汽車就是整個程序,而像發動機,底盤等就是程序中各個小的模塊。只有當各個模塊正常工做,而且暴露的接口和其餘模塊的接口徹底契合,才能組合成一個完整且正常工做的程序。數據結構

模塊化有哪些好處?

固然,若是不將程序分解成一個個獨立的部分,而是整個一大坨,也可以完成所要的功能。那麼爲何教科書還有實際使用中都會提倡模塊化程序設計?這樣作有什麼好處呢?dom

  1. 控制程序設計的複雜度異步

    不知你看過《代碼大全》沒有,裏面有一句很是著名的格言:軟件的首要使命就是管理複雜度。完整的軟件功能複雜度是很是高的,若是不使用有效的方法加以管理,極可能會陷入複雜的泥潭中不可自拔。而將程序分解成模塊,則會將總體功能的複雜度有效的下分到各個模塊中。每一個模塊只要可以管理好本身的複雜度就能夠了。模塊化

  2. 提升代碼的重用性

    仍是以造汽車爲例,假設我造了一個很牛的發動機,多款車型均可以使用它。程序設計也同樣,若是一個模塊可以完成特定的功能,且與父程序耦合度較小,多個程序均可以使用它。

  3. 易於維護和擴展

    小 A 寫了一個程序,並將各個部分劃分的很是明確,再加以人性化的函數命名和註釋。即便有一天小 A 離職了,小 B 要接過來維護以及在此基礎上再開發新的功能也不難。

既然模塊化就這麼多好處, 強大的 python 固然也會吸取這個優秀的設計思想,而且在語言中有所體現,那就是 python 的模塊(module)。

什麼是 python 中的模塊?

先來看一個示例:

  1. 建立 python 文件 a.py,並在文件中定義函數 sum

    def sum(a, b):
        return a + b
  2. 建立 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

來,一塊兒總結下:

  1. python 模塊(module) 是指包含 python 定義(包括 類,函數,變量)和語句的文件(.py作後綴)
  2. 模塊名就是模塊文件名稱去掉.py 後綴
  3. 在模塊內部,能夠經過全局變量 __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 , * 這些符號的組合而已。

模塊的查找順序

在上幾篇文章中已經用瞭如 osshutilsjson 等多個模塊 ,這些模塊都是 python 的內置模塊。相比之下,咱們剛纔使用的 calc 模塊就是自定義模塊。

假設咱們使用 import calc 導入 calc 模塊, python 在啓動時按照什麼樣的順序來查找這個模塊呢?

  1. 先查找內置(built-in)模塊中有沒有,若是沒有轉到 2
  2. 查找 sys.path 變量指定的路徑下有沒有, 有的話就使用,沒有就報錯

sys.path 變量中存儲了那些路徑呢?

  1. 當前運行的 python 腳本所在的目錄

  2. 環境變量 PYTHONPATH 中的路徑,它和 shell 環境變量 PATH 差很少

    這個變量可使用 python 腳本在運行時修改它

  3. 默認的 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 模塊的 plussubstract 方法也展現了出來,那麼 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 中正則表達式,敬請期待。

參考文檔

  1. python modules
  2. 模塊化程序設計—百度百科
  3. 《代碼大全》
  4. 《Python高手之路》

系列文章列表

相關文章
相關標籤/搜索