編寫高質量的Python代碼系列(一)之用Pythonic方式來思考

  Python開發者用Pythonic這個形容詞來描述具備特定風格的代碼。這種風格是你們在使用Python語言進行編程並相互協做的過程當中逐漸造成的習慣。那麼,如何以改風格完成常見的Python編程工做呢?本節將會回答這個問題。前端

  • 第一條:確認本身所用的Python版本python

  • 第二條:遵循PEP8風格指南程序員

  • 第三條:瞭解bytes、str與unicode的區別編程

  • 第四條:用輔助函數來取代複雜的表達式後端

  • 第五條:瞭解切割序列的方法api

  • 第六條:在單次切片操做內,不要同時指定start、end和strideide

  • 第七條:用列表推導來取代map和filter函數

  • 第八條:不要使用含有兩個以上表達式的列表推導ui

  • 第九條:用生成器表達式來改寫數據量較大的列表推導編碼

  • 第十條:儘可能用enumerate取代range

  • 第十一條:用zip函數同時遍歷兩個迭代器

  • 第十二條:不要在for和while循環後面寫else塊

  • 第十三條:合理利用try/exceot/else/finally結構中的每一個代碼塊

第一條:確認本身所用的Python版本

C:\Users\fei\Desktop>python --version
Python 3.6.5

import sys
sys.version
Out[14]: '3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 17:00:18) [MSC v.1900 64 bit (AMD64)]'

 要點

  •    要點1:有兩個版本的Python處於活躍狀態,它們是:Python2和Python3

  •    要點2:有不少中流行的Python運行時環境,例如: Cpython, Jython,IronPython以及PyPy等

  •    要點3:在操做系統的命令行中運行Python時,請確保該Python的版本與你想使用的Python版本相符。

  •    要點4:因爲Python社區把開發重點放在Python3上,因此在開發後序項目時,應該優先考慮採用Python3

第二條:遵循PEP8風格指南

 《Python Enhancement Proposal #8》(8號Python加強提案)又叫PEP8,它是針對Python代碼格式而編訂的風格指南。儘管能夠在保證語法正確的前提下隨意編寫Python代碼,可是採用一致的風格書寫代碼能夠令代碼更加易懂、更加易讀。

完整版PEP8指南

  1. PEP 8 -- Style Guide for Python Code

  2. PEP8中文翻譯

下面列出幾條絕對應該遵照的規則

空白: Python中的空白( whitespace)會影響代碼的含義。 Python程序員使用空白 的時候尤爲在乎,由於它們還會影響代碼的清晰程度。

  • 使用 space(空格)來表示縮進,而不要用tab(製表符)。

  • 和語法相關的每一層縮進都用4個空格來表示。

  • 每行的字符數不該超過79。

  • 對於佔據多行的長表達式來講,除了首行以外的其他各行都應該在一般的縮進 級別之上再加4個空格。

  • 文件中的函數與類之間應該用兩個空行隔開。

  • 在同一個類中,各方法之間應該用一個空行隔開。

  • 在使用下標來獲取列表元素、調用函數或給關鍵字參數賦值的時候,不要在兩 旁添加空格。

  • 爲變量賦值的時候,賦值符號的左側和右側應該各自寫上一個空格,並且只寫 個就好。

命名:PEP8提倡採用不一樣的命名風格來編寫 Python代碼中的各個部分,以便在閱 讀代碼時能夠根據這些名稱看出它們在 Python語言中的角色。

  • 函數、變量及屬性應該用小寫字母來拼寫,各單詞之間如下劃線相連,例如lowercase_underscore。

  • 受保護的實例屬性,應該以單個下劃線開頭,例如, leading underscore

  • 私有的實例屬性,應該以兩個下劃線開頭,例如, double leading underscore

  • 類與異常,應該以每一個單詞首字母均大寫的形式來命名,例如, Capitalized Word 口模塊級別的常量,應該所有采用大寫字母來拼寫,各單詞之間如下劃線相連, 例如, ALL CAPS。

  • 類中的實例方法( instance method)。應該把首個參數命名爲sef,以表示該對象自身

  • 類方法( lass method)的首個參數,應該命名爲cbs,以表示該類自身。

表達式和語句:( The Zen of Python)( Python之禪)中說:「每件事都應該有直白的作 法,並且最好只有一種。」PEP8在制定表達式和語句的風格時,就試着體現了這種思想。

  • 採用內聯形式的否認詞,而不要把否認詞放在整個表達式的前面,例如,應該 寫 if a is not b而不是 if not a is b。

  • 不要經過檢測長度的辦法(如 if len(somelist)=0)來判斷 somalis是否爲目 或」等空值,而是應該採用 if not somelist這種寫法來判斷,它會假定:空值將 自動評估爲 False

  • 檢測 somalis是否爲]或hi等非空值時,也應如此, if somelist語句默認會把 非空的值判斷爲Tme。

  • 不要編寫單行的i語句、for循環、 while循環及cxp複合語句,面是應該把 這些語句分紅多行來書寫,以示清晰。

  • import語句應該老是放在文件開頭。

  • 引入模塊的時候,老是應該使用絕對名稱,而不該該根據當前模塊的路徑來 使用相對名稱。例如,引入bar包中的foo模塊時,應該完整地寫出 from bar import foo,面不該該簡寫爲 import foo。

  • 若是必定要以相對名稱來編寫ipon語句,那就採用明確的寫法: from import food 口文件中的那些mpon語句應該按順序劃分紅三個部分,分別表示標準庫模塊 第三方模塊以及自用模塊:在每一部分之中,各如m語句應該按模塊的字母 順序來排列。

要點

  • 要點1:當編寫Python代碼時,老是應該遵循PEP8風格指南。

  • 要點2:與廣大Python開發者採用同一套代碼風格,可使項目更利於多人協做。

  • 要點3: 採用一致的風格來編寫代碼,能夠令後續的修改工做變得更爲容易。

第三條:瞭解bytes、str與unicode的區別

 Python3有兩種表示字符序列的類型:bytes和str。前者的實例包含原始的8位值;後者的實例包含Unicide字符。

 Python2也有兩種表示字符序列的類型,分別叫作str和unicode。與Python3不一樣的是,str的實例包含原始的8位值;而unicode的實例,則包含Unicode字符。

注:unicode --> 二進制     encode方法

  二進制  --> unicode    decode方法

要點:

  • 要點1:在Python3中,bytes是一種包含8位值的序列,str是一種包含unicoe字符的序列。開發者不能以>或+等操做符來混同操做bytes和str實例

  • 要點2:在Python2中,str是一種包含8位值的序列,unicode是一種包含Unicode字符的序列。若是str只包含有7位ASCII字符,那麼能夠經過相關額操做符同時使用str與unicode。

  • 要點3:在對輸入的數據進行操做以前,使用輔助函數來保證字符序列的類型與開發者的指望相符(有的時候,開發者想操做以UTF-8格式來編碼的8位值,有的時候,則想操做Unicode字符)。

  • 要點4:從文件中讀取二進制數據,或向其中寫入二進制數據時,總應該以‘rb'或‘wb’等二進制模式來開啓文件。

第四條:用輔助函數來取代複雜的表達式

 要點:

  • 要點1:開發者很容易過分運用Python的語法特性,從而寫出那種特別複雜而且難以理解的單行表達式

  • 要點2:請把複雜的表達式移入輔助函數之中,若是要反覆使用相同的邏輯,那就更應該這樣作

  • 要點3:使用if/else表達式,要比用or或and這樣的Boolean操做符寫成的表達式更加清晰。

 

第五條:瞭解切割序列的方法

 要點:

  • 要點1:不要寫多餘的代碼:當start索引爲0,或end索引爲序列長度時,應該將其省略。

  • 要點2:切片操做不會計較start與end索引是否越界,這使得咱們很容易就能從序列的前端或後端開始,對其進行範圍固定的切片操做(如a[:20]或a[-20:])。

  • 要點3:對list賦值的時候,若是使用切片操做,就會把原列表處在相關範圍內的值替換成新值,即便他們的長度不一樣也依然能夠替換。

  • 要點4:若是對賦值操做右側的列表使用切片,而把切片的起止索引都留空,那就會產生一份原列表的拷貝。
  • 要點5:若是對賦值左側的列表使用切片,而又沒有指定起止索引,那麼系統會把右側的新值複製一份,並好用這份拷貝替換左側列表的所有內容,而不會從新分配新的列表。

示例代碼

a = [1,2,3,4,5]
b = a
id(a)
Out[4]: 588602575880
id(b)
Out[5]: 588602575880
c = a[:]
id(c)
Out[7]: 588602487112
a
Out[8]: [1, 2, 3, 4, 5]
id(a)
Out[9]: 588602575880
a[2:4] = [10,11]
id(a)
Out[11]: 588602575880
a
Out[12]: [1, 2, 10, 11, 5]
a[2:4] = [2,2,2,2,2]
a
Out[14]: [1, 2, 2, 2, 2, 2, 2, 5]
id(a)
Out[15]: 588602575880

第六條:在單次切片操做內,不要同時指定start、end和stride

 要點

  • 要點1:既有start和end,又有stride的切割操做,可能會使人費解。

  • 要點2:儘可能使用stride爲整數,切不帶start或end索引的切割操做。儘可能避免用負數作sride.

  • 要點3:再同一個切片操做內,不要同時使用start、end和stride。若是確實須要執行這種操做,那就考慮將其拆解爲兩條賦值語句,其中一角作範圍切割,另外一條作步進切割,或考慮使用內置函數itertools模塊中的islice。

第七條:用列表推導來取代map和filter

  •  要點1: 列表推導比內置的map和filter函數清晰, 由於它無需額外編寫lanbda表達式

  •  要點2:  列表推導能夠跳過輸入列表中的某些元素,若是改用map來作,那就必須輔以filter方能實現

  •  要點3:字典和集合也支持列表推導

第八條:不要使用含有兩個以上表達式的列表推導

  •  要點1: 列表推導支持多級循環,每一級循環也支持多項條件

  •  要點2: 超過兩個表達式的列表推導式很難理解的,應該儘可能避免

第九條:用生成器表達式來改寫數據量較大的列表推導

  •  要點1: 當輸入數據量較大時,列表推導可能會由於佔用太多內存而出問題

  •  要點2: 由生成器表達式所返回的迭代器,能夠逐次產生輸出值,從而避免了內存用量問題。

  •  要點3: 把某個生成器表達式所返回的迭代器,放在另外一個生成器表達式的for子表達式中,便可將兩者組合起來

  •  要點4: 串在一塊兒的生成器表達式執行速度很快。

第十條:儘可能用enumerate取代range

  •  要點1:enumerate函數提供了一種精簡的寫法,能夠在遍歷迭代器時獲知每一個元素的索引

  •  要點2: 儘可能用enumerate來改寫那種將range與下標訪問相結合的序列遍歷代碼

  •  要點3: 能夠給enumerate提供第二個參數,已制定開始技術是所用的值(默認爲0) 

第十一條:用zip函數同時遍歷兩個迭代器

  •  要點1: 內置的zip函數能夠平行的遍歷多個迭代器

  •  要點2: Python3中的zip至關於生成器,會在遍歷過程當中逐次產生元組,而Python2中的zip則是直接把元組徹底生成好,並一次性返回整份列表。

  •  要點3: 若是提供的迭代器長度不等,那麼zip就會自動提早終止

  •  要點4:itertools內置模塊中的zip_longest函數能夠平行地遍歷多個迭代器,而不用在意它們的長度是否相等

第十二條:不要在for和while循環後面寫else塊

  •  要點1: Python有種也屬於語法,可在for及while循環的內部語句塊以後緊跟一個else塊

  •  要點2: 只有當整個循環主體都沒遇到break語句時,循環後面的else塊纔會執行

  • 要點3: 不要在循環後面使用else塊,由於這種寫法既不直觀,又容易引發誤解 

第十三條:合理利用try/exceot/else/finally結構中的每一個代碼塊

  • 要點1: 不管try塊是否發生異常,均可利用try/finally複合語句中的finally塊來執行清理工做

  • 要點2: else塊能夠用來縮減try塊中的代碼量,並把沒有發生異常時所要執行的語句與try/except代碼塊隔開

  • 要點3: 順利運行try塊後,若想使某些操做能在finally塊的清理代碼以前執行,則可將這些操做寫到else塊中。 

相關文章
相關標籤/搜索