當前位置:
首頁 > 最新 > python模塊與包揭秘

python模塊與包揭秘

python模塊與包揭秘


簡介

模塊是最高級別的程序組織單元,它將程序代碼和數據封裝起來以便重用。類似於c語言中include進來的頭文件。在python中,每一個文件就是一個模塊,並且模塊導入其它模塊之後就可以使用導入模塊定義的變數名。

為什麼引入模塊呢?

代碼重用

系統命名空間的劃分

實現共享服務和數據

import如何工作

在閱讀本文之前,想必你一定編寫過這樣的代碼:

我們首先導入了math模塊,然後利用math模塊中的sqrt函數計算了4的平方根。讓我們再細緻一點來看待這個問題。

我在之前提到過,其實import也是執行了一個賦值操作,它把我們需要導入的目標模塊對象賦值給了對應的變數名,例如上例就是把math模塊對象賦值給了math這個變數名,然後math所指向的模塊對象中的內容(函數、最外層的變數)都可以認為是math這個對象的屬性(方法),所以我們可以用object.attr的形式來訪問。

math模塊對象所有的屬性

寫過c語言程序的人都喜歡把python中的import比作c中的,其實這是不太正確的,因為import不只是把一個文件插入到另一個文件,導入時運行時的運算,程序第一次導入指定文件是,會執行三個步驟:

找到模塊文件

編譯成位碼(需要時)

執行模塊的代碼來創建其定義的對象

上面三個步驟都比較好理解,第三步需要記住,第一次導入模塊文件時,模塊文件是會運行的,所以如果你的模塊文件中又print語句之類的就要注意它們是否影響你的程序了,而關於模塊的搜索路徑也需要提一下,如果你不知道可能會出一些小bug。

模塊文件的搜索路徑:

程序主目錄

pythonpath目錄(python的環境變數)

標準鏈接庫目錄

任何.pth文件的內容

pythonpath就是python得一個環境變數,安裝了python的朋友應該都配置了的吧:

這裡寫圖片描述

我之前還真遇到一個與搜索路徑相關的一個bug,當時自己也是啥都不懂(加上有點腦殘),也沒學python,我就隨便編寫了一個文件保存為random.py,然後這個文件中有這麼一段代碼:

random是一個內建模塊,random.choice(a)就是從a列表當中任意取出一個值。本來是很簡單的代碼,但是就是報錯,提示就是random模塊沒有choice這個方法,我當時糾結了好久,真的是腦殘,現在大家應該不會再犯這種錯誤了吧。

想要知道你的機器上python模塊的搜索路徑可以查看sys.path

這裡寫圖片描述導入模塊時的一些細節

我們都知道導入模塊有import與from兩個語句,這兩個語句的區別有必要再次啰嗦一下。首先我們已經知道導入是一種賦值操作。

import語句我們前面也說了,模塊對象被賦值給了一個變數名,然後那個模塊中的內容都是通過【模塊名.屬性】的形式訪問的,也就是被導入模塊的命名空間與當前文件的命名空間是獨立的,沒有相互污染。

這裡寫圖片描述

其中hello模塊的代碼如下:

from與import還是有所差異的,從某種角度來說,這種差異有時候會導致很麻煩的問題。這個差異就體現在,通過from導入的變數名(不應該說是變數名,但是我不知道怎麼形容更好),在當前文件中可以直接訪問,不需要再通過先前說的【模塊名.屬性】的形式,例如上面的例子用from語句重寫:

這裡寫圖片描述

這就意味著被導入模塊中的變數名可能會與當前模塊中的變數名衝突(如果使用from … import * 語句尤其危險),這在一些大型項目中需要特別注意。

主要是注意from … import * 語句

實際上from只是在import的基礎上多做了一次賦值操作而已,例如下面語句:

等價於

重載模塊

模塊只會導入一次,如果想要再次導入需要使用imp.reload函數。注意這裡的重載不是oop中的重載。reload函數主要是讓我們的程序變得更加動態:

只會在第一次導入時,載入和執行該模塊代碼

之後得導入只會使用已載入得模塊對象

reload函數會強制已載入得模塊得代碼重新載入並重新執行。

reload函數可以使程序更加動態,這個動態性體現在哪裡呢?

reload可以只是修改程序的一部分,而無須停止整個程序。不知道大家是否對自己的計算機做過一些配置,該配置需要開機重啟後才能生效?這就是非動態的,如果做成動態的,那麼我們進行的配置,就會在立即生效(或者隔一定時間)。

我們通過一個例子來更直觀的看一下reload的效果:

可以看到hello.py的內容在reload後又執行了一次!

休息一下

前面我們也認識了模塊的常見用法及內部機理,是時候看看包了。實際上python代碼的目錄就稱為包,因此導入目錄就是導入包。事實上,包導入是把計算機上的目錄編程另一個python命名空間,而屬性則對應與目錄中所包含的子目錄或模塊文件。

現在我有如下的目錄結構:

mask.py內容:

test.py內容:

執行test.py效果如下:

這裡寫圖片描述

通過結果可以看到,我們成功導入了mask.py,而且是通過我們之前沒有接觸過的方式,其實from dir0.dir1.dir2 import mask的意思就是從from後面的目錄中導入import語句後面的模塊,這樣做的好處就是可以增加確定性,否則當你的工程過大的時候,在不同目錄下存在同名文件,那麼使用之前的模塊導入的方式就會出現問題。

這個問題是怎麼產生的呢?例如當我自定義了一個文件叫做string.py,然後我另一個文件中有import string這麼一條語句,目的是導入內建模塊string,但是卻導入了我自己寫的string.py,這是由於在python2.7中默認先搜索當前目錄,然後再去pythonpath里尋找,所以當程序執行到import string時,先找到了我自己寫的string.py。

關於包還有一個比較重要的地方,可能大家從我上面給的例子中也發現了這麼一個文件__init__.py,這個文件是把一個目錄變成一個python包的關鍵所在,它裡面可以沒有任何內容,但是必須得存在!否則在進行導入得時候會發生錯誤。那這個文件到底是幹嘛的呢?

其實從它的名字就可以知道它是用作初始化的,python在首次進行包導入時,都會執行相應的__init__.py,所以如果你在這個文件中賦值了一些變數,那麼在包導入之後,這些變數會出現在該包的命名空間中,而且在__init__.py中我們還常定義__all__列表。這個列表中的值指出了執行from ... import *語句時會被導入的屬性。例如在目錄test下有如下__init__.py:

那麼在執行from test import *時,該目錄下的x,y,z模塊都會被導入。當然也可以不設置__all__,它的作用就是可以自定義哪些文件或變數可以使用from *語句導入(否則默認導入該目錄下的所有)。

包相對導入

讀者一定知道相對路徑與絕對路徑吧。這裡的相對導入與絕對導入其實就與他們類似。前面我們說的就是絕對導入,直接給出一個絕對的路徑(其實也是相對路徑,看你怎麼理解),現在我們不想那麼寫,就給出一個相對於當前目錄的包路徑,這樣做就就更加明確了模塊位置

其它

前面提到了通過自定義__all__可以確定哪些模塊再from *時會被導入,其實還有一種方法,可以避免哪些模塊或變數被導入,那就是將他們命名為單下劃線開頭的名稱。

還有一個用的比較廣泛的技巧:每個模塊都有一個__name__屬性,如果一個文件是作為模塊被導入的那麼__name__的值就是它的文件名,如果一個文件是作為主程序運行的(也就是不是被導入的),那麼它的__name__就為__main__

sys.path也就是模塊搜索路徑是可以被程序動態修改的

import與from語句都有一個as功能,就是給導入的包起一個簡短的別名,如:

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 阿信在努力 的精彩文章:

TAG:阿信在努力 |