簡(jiǎn)單來(lái)講,Python程序可以分解成模塊(文件)、語(yǔ)句、表達(dá)式以及對(duì)象(數(shù)據(jù)):
1、程序由模塊組成
2、模塊包含語(yǔ)句
3、語(yǔ)句包含表達(dá)式
4、表達(dá)式建立并處理對(duì)象。
所以,Python 代碼主要由兩部分構(gòu)成:語(yǔ)句和表達(dá)式(函數(shù)、算術(shù)表達(dá)式等)。
語(yǔ)句使用關(guān)鍵字來(lái)組成命令,類似告訴解釋器一個(gè)命令。你告訴 Python 做什么,它就為你做什么,語(yǔ)句可以有輸出,也可以沒(méi)有輸出。
例如: if a > b : print("Hello World!")
而表達(dá)式?jīng)]有關(guān)鍵字。它們可以是使用數(shù)學(xué)運(yùn)算符構(gòu)成的算術(shù)表達(dá)式,也可以是使用括號(hào)調(diào)用的函數(shù)。它們可以接受用戶輸入,也可以不接受用戶輸入,有些會(huì)有輸出,有些則沒(méi)有(在 Python 中未指定返回值的函數(shù)會(huì)自動(dòng)返回 None,等價(jià)于 NULL)。
例如:a*b (1, 23, 34, 45) abs(-4)
一、編碼聲明
Python3中是按照編碼聲明給出的編碼來(lái)讀取源文件的,默認(rèn)值為UTF-8。在此編碼下,全世界大多數(shù)語(yǔ)言的字符可以同時(shí)用在字符串、標(biāo)識(shí)符和注釋中。盡管 Python 標(biāo)準(zhǔn)庫(kù)僅使用英文字母字符做為標(biāo)識(shí)符,這只是任何可移植代碼應(yīng)該遵守的約定。如果要正確的顯示所有的字符,你的編輯器必須能識(shí)別出文件是 UTF-8 編碼,并且它使用的字體能支持文件中所有的字符。
你可以為源文件指定不同的字符編碼。在 #! 行(首行)后插入一行特殊的注釋行來(lái)顯示的定義源文件的編碼:
# -- coding: <encoding-name> --

在Python的文件中,空白行會(huì)被解釋器忽略(但在交互模式提示符下不會(huì))。語(yǔ)句和表達(dá)式中的空格幾乎都忽略(除了在字符串常量?jī)?nèi),以及用在縮進(jìn)時(shí)不會(huì))。# 號(hào)注釋總是忽略。文檔字符串會(huì)忽略,但是會(huì)在運(yùn)行時(shí)自動(dòng)將其附加到對(duì)象上,而且能由文檔工具顯示。
二、邏輯行和物理行
Python程序由多個(gè)邏輯行構(gòu)成,邏輯行的結(jié)束由 NEWLINE 詞符表示。通過(guò)遵循顯式或隱式行連接規(guī)則,一個(gè)邏輯行(Python語(yǔ)法定義的一行語(yǔ)句)可以由一個(gè)或多個(gè)物理行(我們寫(xiě)的代碼實(shí)際所占的一行位置)構(gòu)成。
物理行是由行尾序列(Linux中的換行符 \n)終止的字符序列。Python會(huì)忽略只包含空格,制表符,換行符和注釋的邏輯行(即不生成NEWLINE詞符號(hào))。
顯示行連接(\)
Python 語(yǔ)句,一般使用換行分隔,也就是說(shuō)一個(gè)物理行為一個(gè)邏輯行。兩個(gè)或更多 物理行可以使用反斜杠字符 (\)加入一個(gè)邏輯行,也就是讓一條語(yǔ)句橫跨多行。

注:這種方法已經(jīng)過(guò)時(shí)了,目前從某種程度上來(lái)說(shuō),不再提倡使用這種方法,因?yàn)殛P(guān)注并維護(hù)反斜線比較困難,而且這種做法相當(dāng)脆弱(反斜線之后可能有空格)。
隱式行連接
在使用閉合操作符時(shí),單一語(yǔ)句可以跨多行書(shū)寫(xiě)(一個(gè)邏輯行上的語(yǔ)句存在于多個(gè)物理行)。例如:包含在小括號(hào)( )、中括號(hào)[ ]、花括號(hào){ } 中時(shí)可以多行書(shū)寫(xiě),將要換行的語(yǔ)句可在任何縮進(jìn)層次開(kāi)始,但是最好讓他們和上一行垂直對(duì)其以便于閱讀,Python會(huì)在到達(dá)你輸入的閉合括號(hào) ) 、] 和 } 時(shí)結(jié)束當(dāng)前語(yǔ)句。另外就是三引號(hào)包含下的字符串也可以跨行書(shū)寫(xiě)。
如果要在使用反斜線換行和使用括號(hào)元素?fù)Q行作一個(gè)選擇,我們推薦使用括號(hào),這樣可讀性會(huì)更好。

三、縮進(jìn)
Python的代碼的層次關(guān)系是通過(guò)同樣深度的空格或制表符縮進(jìn)(也就是程序代碼左側(cè)的空白空間)來(lái)體現(xiàn)的,縮進(jìn)程度相同的一組語(yǔ)句構(gòu)成一個(gè)代碼塊(組)。同一代碼塊的代碼行必須嚴(yán)格左對(duì)齊(左邊有同樣多的空格或同樣多的制表符),如果不嚴(yán)格遵守這個(gè)規(guī)則,同一組的代碼就可能被當(dāng)成另一個(gè)組,甚至?xí)?dǎo)致語(yǔ)法錯(cuò)誤。
代碼塊會(huì)在文件末尾或者觸碰到縮進(jìn)量較少的行時(shí)結(jié)束,而更深的嵌套塊就是比所在塊的語(yǔ)句進(jìn)一步向右縮進(jìn)。通常來(lái)說(shuō),頂層(無(wú)嵌套)代碼必須于第一層縮進(jìn)(一般為沒(méi)有縮進(jìn))開(kāi)始。
隨著縮進(jìn)深度的增加,代碼塊的層次也在加深,沒(méi)有縮進(jìn)的代碼塊是最高層次的,別稱為腳本的“main”部分。
使用縮進(jìn)對(duì)齊這種方式組織代碼,不但代碼風(fēng)格優(yōu)雅,而且也大大提高了代碼的可讀性。由于 Python 只使用縮進(jìn)方式表達(dá)代碼塊邏輯,因此“神圣的大括號(hào)戰(zhàn)爭(zhēng)”永遠(yuǎn)不會(huì)發(fā)生在 Python 身上。
注意:由于不同的平臺(tái)的制表符換算成的空格數(shù)并不完全相同,如果你的代碼要跨平臺(tái)應(yīng)用,或者會(huì)被不同的編輯器讀寫(xiě),建議你不要使用制表符。由于使用1個(gè)空格過(guò)短,使用8個(gè)空格又過(guò)長(zhǎng),所以我們常用 4個(gè)空格 來(lái)表示一級(jí)縮進(jìn)。
四、注釋(#)和 文檔字符串
我們使用字符井號(hào) #,來(lái)注釋一個(gè)物理行,注釋可以在一行的任何地方開(kāi)始,解釋器會(huì)忽略掉該行 # 之后的所有內(nèi)容(只要#不是位于字符串常量中)。



# 注釋只能在源代碼中看到,要編寫(xiě)能夠更廣泛使用的注釋,請(qǐng)使用文檔字符串。通常來(lái)講,文檔字符串最適于大型功能的文檔(例如,“我的文件、類、函數(shù)做這些事”),而 # 注釋最適合用于較小功能的文檔(例如:“這個(gè)表達(dá)式做這些事”)
五、同一行書(shū)寫(xiě)多個(gè)語(yǔ)句(;)
分號(hào);允許你將多個(gè)語(yǔ)句寫(xiě)在同一行上,語(yǔ)句之間用分號(hào)隔開(kāi)。而這些語(yǔ)句不能是復(fù)合語(yǔ)句也就是說(shuō)不能在這行開(kāi)始一個(gè)新的代碼塊。

注意: 同一行上書(shū)寫(xiě)多個(gè)語(yǔ)句會(huì)大大降低代碼的可讀性, Python 雖然允許但不提倡你這么做。
六、模塊
每一個(gè) Python 文件都可以被當(dāng)成是一個(gè)模塊,模塊以磁盤(pán)文件的形式存在。當(dāng)一個(gè)模塊變得過(guò)大,并且驅(qū)動(dòng)了太多功能的話,就應(yīng)該考慮拆一些代碼出來(lái)另外建一個(gè)模塊。模塊里的代碼可以是一段直接執(zhí)行的腳本,也可以是一堆類似庫(kù)函數(shù)的代碼,從而可以被別的模塊導(dǎo)入(import)調(diào)用。
七、文檔
Python 還提供了一個(gè)機(jī)制,可以通過(guò) doc 特別變量,動(dòng)態(tài)獲得文檔字串。在模塊、類聲明或函數(shù)聲明中,Python會(huì)自動(dòng)的封裝第一個(gè)沒(méi)有賦值的字符串(通常使用三對(duì)引號(hào)表示)并賦值給對(duì)應(yīng)對(duì)象的 obj.doc 屬性,使得我們可以在程序運(yùn)行時(shí)可以通過(guò)訪問(wèn)對(duì)象的doc屬性查看對(duì)象的文檔說(shuō)明。其中 obj是一個(gè)模塊,類,或函數(shù)的名字。

Python還在標(biāo)準(zhǔn)庫(kù)中附帶了PyDoc工具,它知道如何提取文檔字符串并且自動(dòng)提取其結(jié)構(gòu)化的信息,并將其格式化成各種類型的排列友好的報(bào)表。我們可以是引用內(nèi)置的help() 函數(shù)來(lái)調(diào)用PyDoc提供的接口,從而生成簡(jiǎn)單的文字報(bào)表。

八、模塊(文件)結(jié)構(gòu)和布局
用模塊(文件)來(lái)合理組織你的 Python 代碼是簡(jiǎn)單又自然的方法。 你應(yīng)該建立一種統(tǒng)一且容易閱讀的結(jié)構(gòu),并將它應(yīng)用到每一個(gè)文件中去。下面就是一種非常合理的布局。

(1) 起始行
通常只有在類 Unix 環(huán)境下才使用起始行,有起始行就能夠僅輸入腳本名字來(lái)執(zhí)行腳本,無(wú)需直接調(diào)用解釋器。
(2)模塊文檔
簡(jiǎn)要介紹模塊的功能及重要全局變量的含義,模塊外可通過(guò) module.__doc__ 訪問(wèn)這些內(nèi)容。
(3)模塊導(dǎo)入
導(dǎo)入當(dāng)前模塊的代碼需要的所有模塊。每個(gè)模塊僅導(dǎo)入一次(當(dāng)前模塊被加載時(shí)),函數(shù)內(nèi)部的模塊導(dǎo)入代碼不會(huì)被執(zhí)行, 除非該函數(shù)正在執(zhí)行。
(4)變量定義
這里定義的變量為全局變量, 本模塊中的所有函數(shù)都可直接使用。從好的編程風(fēng)格角度說(shuō),除非必須,否則就要盡量使用局部變量代替全局變量,如果堅(jiān)持這樣做,你的代碼就不但容易維護(hù),而且還可以提高性能并節(jié)省內(nèi)存。
(5)類定義語(yǔ)句
所有的類都需要在這里定義。當(dāng)模塊被導(dǎo)入時(shí) class 語(yǔ)句會(huì)被執(zhí)行, 類也就會(huì)被定義。類的文檔變量是 class.__doc__ 。
(6)函數(shù)定義語(yǔ)句
此處定義的函數(shù)可以通過(guò) module.function() 在外部被訪問(wèn)到,當(dāng)模塊被導(dǎo)入時(shí) def 語(yǔ)句會(huì)被執(zhí)行, 函數(shù)也就都會(huì)定義好,函數(shù)的文檔變量是 function.__doc__ 。
(7) 主程序
無(wú)論這個(gè)模塊是被別的模塊導(dǎo)入還是作為腳本直接執(zhí)行,都會(huì)執(zhí)行這部分代碼。通常這里不會(huì)有太多功能性代碼,而是根據(jù)執(zhí)行的模式調(diào)用不同的函數(shù)。
注意: 主程序調(diào)用 main()函數(shù)
主程序代碼通常都和你前面看到的代碼相似,檢查 name 變量的值然后再執(zhí)行相應(yīng)的調(diào)用。主程序中的代碼通常包括變量賦值, 類定義和函數(shù)定義,隨后檢查 name 來(lái)決定是否調(diào)用另一個(gè)函數(shù)(通常調(diào)用 main()函數(shù))來(lái)完成該模塊的功能。主程序通常都是做這些事。 不管用什么名字, 我們想強(qiáng)調(diào)一點(diǎn)那就是:這兒是放置測(cè)試代碼的好地方。大部分的 Python 模塊都是用于導(dǎo)入調(diào)用的,直接運(yùn)行模塊應(yīng)該調(diào)用該模塊的回歸測(cè)試代碼。
很多項(xiàng)目都是只有一個(gè)主程序,由它導(dǎo)入所有需要的模塊。所以請(qǐng)記住,絕大部分的模塊創(chuàng)建的目的是為了被別人調(diào)用而不是作為獨(dú)立執(zhí)行的腳本。 我們也很可能創(chuàng)建一個(gè) Python 庫(kù)風(fēng)格的模塊,這種模塊的創(chuàng)建目的就是為了被其他模塊調(diào)用??傊?,只有一個(gè)模塊,也就是包含主程序的模塊會(huì)被直接執(zhí)行,或由用戶通過(guò)命令行執(zhí)行,或作為批處理執(zhí)行, 或由 Unix cron 任務(wù)定時(shí)執(zhí)行,或通過(guò) Web 服務(wù)器調(diào)用,或通過(guò) GUI 執(zhí)行。
時(shí)刻記住一個(gè)事實(shí),那就是所有的模塊都有能力來(lái)執(zhí)行代碼。最高級(jí)別的 Python 語(yǔ)句,也就是說(shuō), 那些沒(méi)有縮進(jìn)的代碼行在模塊被導(dǎo)入時(shí)就會(huì)執(zhí)行, 不管是不是真的需要執(zhí)行。由于有這樣一個(gè)“特性”,比較安全的寫(xiě)代碼的方式就是除了那些真正需要執(zhí)行的代碼以外, 幾乎所有的功能代碼都在函數(shù)當(dāng)中。再說(shuō)一遍, 通常只有主程序模塊中有大量的頂級(jí)可執(zhí)行代碼,所有其它被導(dǎo)入的模塊只應(yīng)該有很少的頂級(jí)執(zhí)行代碼,所有的功能代碼都應(yīng)該封裝在函數(shù)或類當(dāng)中。
注意: __name__ 指示模塊應(yīng)如何被加載
由于主程序代碼無(wú)論模塊是被導(dǎo)入還是被直接執(zhí)行都會(huì)運(yùn)行, 我們必須知道模塊如何決定運(yùn)行方向。一個(gè)應(yīng)用程序可能需要導(dǎo)入另一個(gè)應(yīng)用程序的一個(gè)模塊,以便重用一些有用的代碼(否則就只能用拷貝粘貼那種非面向?qū)ο蟮挠薮朗侄危?這種情況下,你只想訪問(wèn)那些位于其它應(yīng)用程序中的代碼,而不是想運(yùn)行那個(gè)應(yīng)用程序。因此一個(gè)問(wèn)題出現(xiàn)了,“Python 是否有一種方法能在運(yùn)行時(shí)檢測(cè)該模塊是被導(dǎo)入還是被直接執(zhí)行呢?” 判斷 name 系統(tǒng)變量就是正確答案。
如果模塊是被導(dǎo)入, name 的值為模塊名字
如果模塊是被直接執(zhí)行, name 的值為 ' main '
九、在主程序中書(shū)寫(xiě)測(cè)試代碼
優(yōu)秀的程序員和軟件工程師,總是會(huì)為我們的應(yīng)用程序提供一組測(cè)試代碼或者簡(jiǎn)單教程。對(duì)那些僅僅為了讓別的程序?qū)攵鴦?chuàng)建的模塊來(lái)說(shuō), Python 有效的簡(jiǎn)化了這個(gè)任務(wù)。這些模塊理論上永遠(yuǎn)不會(huì)被直接執(zhí)行, 那么,可以讓這個(gè)模塊在被直接執(zhí)行時(shí)進(jìn)行系統(tǒng)測(cè)試,而且這樣設(shè)置起來(lái)一點(diǎn)兒也不難。
測(cè)試代碼僅當(dāng)該文件被直接執(zhí)行時(shí)運(yùn)行, 也就是說(shuō)不是被別的模塊導(dǎo)入時(shí)。上文及核心筆記中提到如何判斷一個(gè)模塊是被直接運(yùn)行還是被導(dǎo)入。我們應(yīng)該利用 name 變量這個(gè)有利條件。將測(cè)試代碼放在一個(gè)或者叫 main(), 或者叫 test()(或者你隨便取個(gè)啥名字)的函數(shù)中,如果該模塊是被當(dāng)成腳本運(yùn)行, 就調(diào)用這個(gè)函數(shù)。
這些測(cè)試代碼應(yīng)該隨著測(cè)試條件及測(cè)試結(jié)果的變更及時(shí)修改, 每次代碼更新都應(yīng)該運(yùn)行這些測(cè)試代碼,以確認(rèn)修改沒(méi)有引發(fā)新問(wèn)題。只要堅(jiān)持這樣做,你的代碼就會(huì)足夠健壯,更不用提驗(yàn)證和測(cè)試新特性和更新了。
在主程序中放置測(cè)試代碼是測(cè)試模塊的簡(jiǎn)單快捷的手段。Python 標(biāo)準(zhǔn)庫(kù)中還提供了unittest 模塊, 有時(shí)候它被稱為 PyUnit, 是一個(gè)測(cè)試框架。當(dāng)需要對(duì)一個(gè)大系統(tǒng)的組件進(jìn)行正規(guī)系統(tǒng)的回規(guī)測(cè)試時(shí),它就會(huì)派上場(chǎng)。
《Python基礎(chǔ)手冊(cè)》系列:
Python基礎(chǔ)手冊(cè) 1 —— Python語(yǔ)言介紹
Python基礎(chǔ)手冊(cè) 2 —— Python 環(huán)境搭建(Linux)
Python基礎(chǔ)手冊(cè) 3 —— Python解釋器
Python基礎(chǔ)手冊(cè) 4 —— 文本結(jié)構(gòu)
Python基礎(chǔ)手冊(cè) 5 —— 標(biāo)識(shí)符和關(guān)鍵字
Python基礎(chǔ)手冊(cè) 6 —— 操作符
Python基礎(chǔ)手冊(cè) 7 —— 內(nèi)建函數(shù)
Python基礎(chǔ)手冊(cè) 8 —— Python對(duì)象
Python基礎(chǔ)手冊(cè) 9 —— 數(shù)字類型
Python基礎(chǔ)手冊(cè)10 —— 序列(字符串)
Python基礎(chǔ)手冊(cè)11 —— 序列(元組&列表)
Python基礎(chǔ)手冊(cè)12 —— 序列(類型操作)
Python基礎(chǔ)手冊(cè)13 —— 映射(字典)
Python基礎(chǔ)手冊(cè)14 —— 集合
Python基礎(chǔ)手冊(cè)15 —— 解析
Python基礎(chǔ)手冊(cè)16 —— 文件
Python基礎(chǔ)手冊(cè)17 —— 簡(jiǎn)單語(yǔ)句
Python基礎(chǔ)手冊(cè)18 —— 復(fù)合語(yǔ)句(流程控制語(yǔ)句)
Python基礎(chǔ)手冊(cè)19 —— 迭代器
Python基礎(chǔ)手冊(cè)20 —— 生成器
Python基礎(chǔ)手冊(cè)21 —— 函數(shù)的定義
Python基礎(chǔ)手冊(cè)22 —— 函數(shù)的參數(shù)
Python基礎(chǔ)手冊(cè)23 —— 函數(shù)的調(diào)用
Python基礎(chǔ)手冊(cè)24 —— 函數(shù)中變量的作用域
Python基礎(chǔ)手冊(cè)25 —— 裝飾器
Python基礎(chǔ)手冊(cè)26 —— 錯(cuò)誤 & 異常
Python基礎(chǔ)手冊(cè)27 —— 模塊
Python基礎(chǔ)手冊(cè)28 —— 模塊的高級(jí)概念
Python基礎(chǔ)手冊(cè)29 —— 包