這是我見過最完整的模塊資料!Python有這樣的資源,很快就入門!
一、模塊
1、模塊和導入
當程序代碼量變得相當大、邏輯結構變得非常複雜的時候,我們最好把代碼按照邏輯和功能劃分成一些有組織的代碼塊,並將其保存到一個個獨立的文件當中。這些文件可以包含可執行代碼、函數、類或者這些東西的組合,這些自我包含並且有組織的代碼塊就是模塊( module )。模塊是最高級別的 Python 代碼組織單元。
模塊往往對應於物理機上的 Python 文件(或者是用外部語言如C、Java或C#編寫而成的擴展)。當你創建了一個 Python 源文件,其對應的模塊的名字就是不帶 .py 後綴的文件名。一個模塊(Python程序文件)創建之後, 你可以從另一個文件中使用 import 語句導入這個模塊來使用,從而實現代碼的重用。這個把其他模塊附加到你的代碼中的操作叫做導入( import )。導入其他模塊之後就可以使用導入的模塊中定義的變數名。
2、模塊的作用
(1.代碼重用
因為模塊對應於 Python 文件,所以模塊中的代碼可以永久保存。你可以按照需要在代碼中任意次數的使用導入的模塊中定義的變數名(函數、類等),甚至可以重新導入模塊。除了作為最高級別的 Python 代碼組織單元,模塊(以及 模塊包)也是 Python 中程序代碼重用的最高層次。
(2.系統命名空間的劃分
模塊還是定義變數名的空間,其內部定義的變數名作為模塊的屬性,可以通過導入被多個外部的文件中的代碼引用。
模塊將變數名封裝進了自己的命名空間,這一點對避免變數名的衝突很有幫助。所有的一切都存在於 」模塊「 中,可執行的代碼以及創建的對象都毫無疑問的封裝在模塊之中。正式由於這一點,模塊是組織系統組件的天然工具。
(3.實現共享服務和數據
從操作的角度來看,模塊對實現跨系統共享的組件是很方便的,只需要在不同的文件中導入相同的模塊即可。
3、Python 的程序架構
一個 Python 程序通常都不僅僅涉及一個文件,一般都會採用多文件系統的形式。即使編寫單個文件,幾乎也一定會導入標準庫模塊或者使用到其他人已經寫好的外部文件。
一般來講一個完整的程序由啟動運行的腳本文件以及零個或多個作為支持(用作導入)的文件組成。
在 Python 中,頂層文件包含了程序的主要的控制流程:這就是你需要運行來啟動程序的文件。作為模塊被導入的文件通常在運行時不需要直接做任何事,它提供了頂層文件運行所需要的各種組件(普通變數、函數、類等)。頂層文件使用了在模塊文件中定義的組件,而這些模塊使用了其他模塊所定義的組件。
在 Python 中,一個文件導入了一個模塊來獲得這個模塊中定義的變數的訪問權,這些變數被認作是這個模塊的屬性。導入的概念在 Python 之中貫穿始末。任何文件都能從任何其他文件中導入其變數,導入鏈要多深就有多深。
4、標準庫模塊
Python 自帶了很多實用的模塊,稱為標準鏈接庫。這個集合體大約有200多個模塊,包含與平台不相關(不依賴於具體的系統,可以在任何系統上以同樣的方式調用,也就是說這些標準庫模塊是跨平台的)的常見程序設計任務:操作系統介面、對象永久保存、文字模式匹配、網路和 internet 腳本、GUI 建構等。
這些工具都不是 Python 語言的組成部分,但是,你可以在任何安裝了 Python 的環境中,導入適當的模塊來使用。因為這些都是標準庫模,所以他們一定可以用,而且在執行 Python 的絕大數平台上都可以運行。
二、模塊的導入
模塊中的代碼會在首次導入時執行,首先建立空的模塊對象,然後按照從頭到尾的順序,逐一執行該模塊內的語句。頂層(不在def或class之內)的賦值語句(例如,=、def 和 class等)生成的變數會成為模塊對象的屬性,這些變數名會存儲在模塊的命名空間內。模塊的命名空間能通過屬性 __dict__ 或內建函數 dir() 獲取。
1、模塊文件的命名
任何以 「.py」 為後綴名的 Python 文件都會被自動認為是 Python 模塊,一般來說,Python 文件怎麼命名都可以,但是如果打算將其作為模塊導入,文件必須以 」.py「 結尾。
對於會執行但不會被導入的頂層文件而言,.py 後綴從技術上來說是可有可無的,但是每次都加上去,可以確保文件類型更醒目,並使其以後可以被導入到任何文件中。
因為模塊名在 Python 程序中會變成變數名(沒有.py)。因此Python文件應該遵循普通變數名的命名規則。事實上,包導入中所用的模塊的文件名和目錄名都必須遵循變數名規則。
2、導入模塊的步驟
在Python中,導入並不是把一個文件文本插入另一個文件中。導入其實是運行時的運算,程序第一次導入指定文件時,會執行三個步驟。
搜索找到模塊文件。
編譯成位元組碼(需要時)。
執行模塊的代碼來創建其所定義的對象,定義 import 語句所在文件的作用域的局部命名空間中的一個或多個變數名。
這三個步驟只在模塊第一次導入時才會執行。在這之後,導入相同模塊時,會跳過這三個步驟,而只是提取內存中已載入的模塊對象。這是有意而為之的,因為該操作開銷較大。如果你在模塊已載入後還需要再次導入(例如,為了支持終端用戶的定製),你就得通過調用reload()強制導入模塊。
從技術上講,Python 把載入的模塊存儲到一個名為 sys.modules 的表中,並在導入操作的開始檢查該表。如果模塊不存在,將會自動執行上面的三個步驟。
搜索
Python 會遍歷模塊搜索路徑,查找 import 語句所引用的模塊文件。在導入者文件中,只能列出要導入的模塊文件的簡單名稱,路徑和後綴是刻意省略掉的。
當一個模塊被導入時,Python 會把程序內部的模塊名映射到外部物理環境中的文件名,也就是將模塊搜索路徑中的目錄路徑添加在模塊名前邊,並在模塊名的後邊添加 .py 或其他後綴名。
編譯
找到模塊文件後,Python 會查找對應的 .pyc 位元組碼文件。如果沒有位元組碼文件,Python 會將模塊文件編譯成位元組碼文件。如果找到對應的位元組碼文件,Python 會檢查文件的時間戳,如果發現位元組碼文件比模塊文件舊(例如,如果你修改過源文件),就會重新編譯模塊文件生成新的位元組碼文件。如果位元組碼文件不比對應的 .py 源代碼文件舊,就會跳過源代碼到位元組碼的編譯步驟。
如果 Python 在搜索路徑上只發現了位元組碼文件,而沒有源代碼,就會直接載入位元組碼文件(這意味著你可以把一個程序只作為位元組碼文件發布,而避免發送源代碼)。換句話說,直接使用位元組碼文件跳過編譯步驟,會提高程序的啟動提速。
通常不會看見程序頂層文件的 .pyc 位元組碼文件,除非這個文件也別其他文件導入:只有被導入的文件才會在機器上留下 .pyc 。頂層文件的位元組碼是在內部使用後就丟棄了,被導入文件的位元組碼則保存在文件中從而可以提高之後導入的速度。
頂層文件通常是設計成直接執行,而不是被導入的。
運行
import 操作的最後步驟是執行模塊的位元組碼。文件中所有語句會從頭到尾依次執行,而此步驟中任何對變數名的賦值運算,都會產生模塊文件的屬性。因此,這個執行步驟會生成模塊代碼所定義的所有工具。
因為最後的導入步驟實際上是執行文件的程序代碼,如果模塊文件中任何頂層代碼確實做什麼實際的工作,你就會在導入時看見其結果。
3、import 語句
常見的import導入語句可以分為兩種:單獨的 import 語句用來導入模塊名;帶有
from 的 import 語句用來導入模塊中的變數名,同時可以使用 號導入模塊中的所有變數。在以上兩種語句中,我們都可以使用as語句為導入的模塊或變數指定別名。當語句包含多個子句(以逗號分隔)時,為每個子句分別執行模塊導入的三個步驟,就像子句已被分隔為單獨的 import 語句一樣。
如果導入的模塊被成功檢索到,它將通過以下三種方式之一綁定到本地命名空間:
如果模塊名後面是as,則 as 之後的變數名將在本地命名空間中綁定為對導入的模塊對象的引用。
如果未指定其他名稱,並且正在導入的模塊是頂級模塊(),則模塊的名稱將在本地命名空間中綁定為對導入模塊對象的引用。
如果正在導入的模塊不是頂級模塊,則包含該模塊的頂級包的名稱在本地命名空間中被綁定為對頂級包的引用。導入的模塊必須使用其完全限定名稱而不能直接訪問。包的概念會在後續章節介紹。
from 形式會多一些複雜的過程:
找到 from 子句中指定的模塊,如果需要,載入和初始化它;
對於 import 子句中指定的每個標識符:
a. 檢查導入的模塊是否具有該名稱的屬性;
b. 如果沒有,請嘗試導入具有該名稱的子模塊,然後再次檢查導入的模塊的該屬性;
c. 如果未找到該屬性,則引發 ImportError;
d. 如果找到該名稱的屬性,對該屬性的引用存儲在本地命名空間中,使用 as 子句中的名稱(如果存在),否則使用屬性名稱;
如果在 from 語句中 import 後面的標識符列表被替換為星號(*),則模塊中定義的所有公共名稱都在 import 語句所在的作用域的本地命名空間中綁定。
(1)import 形式
import 語句將模塊導入文件中:
import module_name
import 是可執行語句,就像 def 一樣,它是隱性的賦值語句。當 Python 執行到這個語句時,會將導入生成的模塊對象賦值給 import 語句後面的模塊名,而模塊文件頂層對任意類型賦值了的變數名,都會產生為模塊對象的屬性。
一旦導入完成,一個模塊的屬性(函數和變數)可以通過熟悉的 (. )句點屬性標識法訪問。
module.function()
module.variable
import 語句組合兩個操作;它搜索指定的模塊並根據需要執行模塊以得到模塊對象,然後將模塊對象綁定到本地作用域中的模塊名。
import 語句的搜索操作被定義為:使用適當的參數調用 __import__() 函數。直接調用 __import__() 只執行模塊搜索,如果找到,則執行模塊創建操作,並返回模塊對象。如果找不到指定的模塊,則會引發 ImportError。雖然可能會伴隨著某些其他的操作,例如導入父包以及更新各種緩存(包括sys.modules),但只有 import 語句會執行名稱綁定操作。
屬性名的點號運算
在 Python 之中,可以使用點號運算語法 object.attribute 獲取任意的 object 的attribute 屬性。
點號運算符其實就是表達式,傳回和對象相配的屬性名的值。當使用點號運算符來讀取變數名時,就把明確的對象提供給 Python , LEGB 規則只適用於無點號運算的純變數名。
簡單變數名
X 是指在當前作用域內搜索變數名 X(遵循LEGB規則)
點號運算
X,Y 是指在當前範圍內搜索 X,然後搜索對象 X 之中的屬性 Y(而非在作用域里)。
多層點號運算
X,Y,Z 指的是在當前範圍內搜索 X,然後搜索對象 X 之中的屬性 Y,然後在對象X.Y 中搜索屬性 Z 。
通用性
點號運算可用於任何具有屬性的對象:模塊、類、C 擴展類型等。
(2)from - import 形式
使用 from-import 語句可以將模塊的屬性導入到當前作用域,並綁定到指定的變數名。
from module import name1[, name2[,... nameN]]
和 import 一樣,from - import 語句也是可執行的隱性賦值語句。import 將導入的模塊對象賦值給一個模塊名。而 from - import 將模塊中的一個或多個變數(也就是生成的模塊對象的一個或多個屬性)綁定到當前文件中 import 語句指定的變數名。因為 from 會把模塊中定義的變數名複製到另一個文件的作用域中,所以它就可以讓我們直接在另一個文件中直接使用從模塊中導入的變數名,而不需要通過模塊名。(例如:variate)
from 的第一步驟也是普通的導入操作。因此,from 總是會把整個模塊導入到內存中(如果還沒被導入的話),無論是從這個文件中複製出多少變數名。只載入模塊文件的一部分(例如,一個函數)是不可能的。但是因為模塊在 Python 之中是位元組碼而不是機器碼,通常可以忽略效率的問題。
from 語句潛在的陷阱
因為 from 語句會讓變數位置更隱秘和模糊,所以 form 語句可能會破壞命名空間。如果使用 from 導入變數,而那些變數碰巧和作用域中現有變數同名,變數就會被悄悄地覆蓋掉。使用簡單的 import 語句就不會有這種問題,因為你一定得通過模塊名才能獲取其屬性(變數名)。不過使用 from 時,只要你了解並預料到可能發生這種事,在實際情況下這就不是一個大問題了,尤其當你明確列出導入的變數名時(例如,from moudle import a, b, c)。
和 reload 調用同時使用時,from 語句有比較嚴重的問題,因為導入的變數名可能引用之前導入的對象。
簡單模塊一般傾向於使用 import,而不是 from。多數的 from 語句是用於明確列舉出想要的變數,而且限制在每個文件中只用一次 from * 形式。當你必須使用兩個不同模塊內定義的相同的變數名時,才真的必須使用 import,這種情況下不能用 from(當然你可以在 from 語句中使用 as 語句來個規避變數名衝突的問題)。
(3)from - import * 形式
從一個模塊導入許多變數名時,import 行會越來越長,直到自動換行,而且我們需要使用反斜杠字元 讓一條語句橫跨多行 。
from module import name1, name2, name3, name4, ame5, name6, name7
你可以選擇使用多行的 from-import 語句:
from module import name1, name2, name3, name4from module import name5, name6, name7
在 from 語句的 import 子句中,當我們使用 * 時,會取得模塊頂層所有賦值的變數名的拷貝。從根本上來說,這就是把一個模塊的命名空間融入另一個模塊之中;同樣地,實際效果就是可以讓我們少輸入一些代碼。from * 語句形式只能用在一個模塊文件的頂部,嘗試在類或函數定義中使用它將引發 SyntaxError。
核心風格: 限制使用 " from - import * "
在實踐中, 我們認為 "from - import *" 不是良好的編程風格,因為它"污染"當前名稱空間,讓變數名難以理解。而且很可能覆蓋當前名稱空間中現有的名字,尤其是在導入一個以上的模塊時。事實上,from * 形式會把一個命名空間融入到另一個,所以會使得模塊的命名空間的分割特性失效。
如果某個模塊有很多要經常訪問的變數或者模塊的名字很長,這也不失為一個方便的好辦法。我們只在兩種場合下建議使用這樣的方法,一個場合是:要使用的目標模塊中的屬性非常多,反覆鍵入模塊名很不方便,例如 Tkinter (Python/Tk) 和 NumPy (Numeric Python) 模塊,可能還有 socket 模塊。另一個場合是在交互解釋器下,因為這樣可以減少輸入次數。
一般情況下,我們不提倡使用不再流行的 from module import * 語句 。真正的 Python 程序員應該使用 Python 的標準分組機制(圓括弧)來創建更合理更明確的多行導入語句。
最小化 from * 的破壞:_x 和 __all__
把下劃線放在變數名前面(例如,_x),可以防止客戶端使用 from * 語句導入模塊名時,把其中的那些變數名複製出去。這其實是為了對命名空間的破壞最小化而已。下劃線不是私有變數的聲明:你還是可以使用其他導入形式看見並修改這類變數名。
此外,你也可以在模塊頂層把變數名的字元串列表賦值給變數名 __all__ ,以達到類似於 _x 命名慣例的隱藏效果。
使用此功能時,from * 語句只會把列在 __all__ 列表中的這些變數名賦值出來。事實上這和 x 慣例相反 __all 時指出要複製的變數名,而_x 是指出不被複制的變數名。Python 會先尋找模塊內的 __all _ 列表;如果沒有定義的話,from * 就會複製出開頭沒有單下劃線的所有變數名。
就像 _x 慣例一樣,__all__ 列表只對 from * 語句這種形式有效,它並不是私有聲明。
(4)擴展的導入語句(as)
有時候你導入的模塊名或是模塊屬性名稱已經在你的程序中使用了,或者你不想使用導入的名字,可能是它太長不便輸入什麼的。 這已經成為 Python 程序員的一個普遍需求:使用自己想要的名字替換模塊的原始名稱。使用擴展的as子句,你就可以在導入的同時指定局部綁定名稱。
import 語句和 from 語句都可以擴展,讓模塊可以在腳本中給予不同的變數名。
import modulename as name相當於:import modulenamename = modulenamedel modulenamefrom modulename import attrname as name相當於:from modulename import attrnamename = attrnamedel attrname
這個擴展功能很常用,替代變數名較長的變數提供簡短一些的同義詞,而且當已在腳本中使用一個變數名使得執行普通 import 語句會被覆蓋時,使用 as,就可避免變數名衝突。
4、模塊重載
在同一個進程中模塊只在第一次導入時,載入和執行該模塊的代碼。之後的導入只會使用已載入的模塊對象,而不會重載或重新執行文件的代碼。要強制使模塊重新載入並重新運行,可以使用 reload() 函數。
reload()
reload() 函數位於Python中的imp模塊內,使用前必須先導入。它會強制已載入的模塊的代碼重新載入並重新執行。因為 reload() 期望得到的是對象,在重載之前,模塊一定是已經預先成功導入了。
重新執行模塊文件的代碼會覆蓋其現有的命名空間。重載會影響所有使用 import 導入模塊的程序,因為使用 import 的程序需要通過點號運算符取出屬性,在重載後,使用的模塊對象變成了新的值。重載只會對重載後使用 from 語句導入模塊的程序造成影響。之前使用 from 來讀取屬性的客戶端並不會受到重載的影響,那些程序引用的依然是重載前所取出的舊對象。
reload() 函數使得可以修改模塊程序的一些代碼,而無須停止整個程序。因此,利用reload() ,可以立即看到對模塊的修改效果。重載無法用於每種情況,但是能用時,可縮短開發的流程。一般的用法是:導入一個模塊,在文本編輯器內修改其源代碼,然後將其重載。當調用 reload() 時,Python 會重讀模塊文件的源代碼,重新執行其頂層語句。
因為 Python 是解釋性的(或多或少),其實已經避免了類似 C 語言程序執行時所需的編譯連接步驟:在執行程序導入時,模塊會動態載入。重載進一步的提供了性能優勢,讓你可以修改執行中的程序的一部分,而不需要中止。注意:reload() 當前只能用在Python 編寫的模塊;用 C 這類語言編寫的編譯後的擴展模塊也可在執行中動態載入,但無法重載。


※22歲轉行新手學了34天Python還沒入門的原因:學習路線不對!
※Python代碼技巧,你值得擁有!
TAG:Python |