python編寫規範-pep8
本文參考文獻1,結合文獻2(文獻2和其它部分會穿插到其中,後面加上註釋)
二、A Foolish Consistency is the Hobgoblin of Little Minds 盡信書則不如無書
3.3 Maximum Line Length 行的最大長度
3.4 Should a line break before or after a binary operator 在二元運算子之前應該換行嗎
3.6 Source File Encoding 原始檔編碼
3.8 Module level dunder names 模組級的呆名
四、Whitespace in Expressions and Statements 表示式和語句中的空格
4.2 Other Recommendations 其他建議
5.3 Documentation Strings 文件字串
6.1 Overriding Principle 最重要的原則
6.2 Descriptive Naming Styles 描述命名風格
6.3 Prescriptive Naming Conventions 約定俗成命名約定
Package and Module Names 包名和模組名
Function and method arguments 函式和方法引數
Method Names and Instance Variables 方法名和例項變數
Designing for inheritance 繼承的設計
6.4 Public and internal interfaces 公共和內部的介面
七、Programming Recommendations 程式設計建議
一、Introduction 介紹
本文提供的Python程式碼編碼規範基於Python主要發行版本的標準庫。Python的C語言實現的C程式碼規範請檢視相應的PEP指南1。
這篇文件以及PEP 257(文件字串的規範)改編自Guido原始的《Python Style Guide》一文,同時添加了一些來自Barry的風格指南2。
這篇規範指南隨著時間的推移而逐漸演變,隨著語言本身的變化,過去的約定也被淘汰了。
許多專案有自己的編碼規範,在出現規範衝突時,專案自身的規範優先。
說明:
PEP是 Python Enhancement Proposal 的縮寫,翻譯過來就是 Python增強建議書 。
PEP8譯者:本文基於 2013-08-02 最後修改的 PEP8 版本翻譯,若要檢視英文原文,請參考PEP8
二、A Foolish Consistency is the Hobgoblin of Little Minds 盡信書則不如無書
Guido的一條重要的見解是程式碼閱讀比寫更加頻繁。這裡提供的指導原則主要用於提升程式碼的可讀性,使得在大量的Python程式碼中保持一致。就像PEP 20提到的,"Readability counts"。
這是一份關於一致性的風格指南。這份風格指南的風格一致性是非常重要的。更重要的是專案的風格一致性。在一個模組或函式的風格一致性是最重要的。
然而,應該知道什麼時候應該不一致,有時候編碼規範的建議並不適用。當存在模稜兩可的情況時,使用自己的判斷。看看其他的示例再決定哪一種是最好的,不要羞於發問。
特別是不要為了遵守PEP約定而破壞相容性!
幾個很好的理由去忽略特定的規則:
- 當遵循這份指南之後程式碼的可讀性變差,甚至是遵循PEP規範的人也覺得可讀性差。
- 與周圍的程式碼保持一致(也可能出於歷史原因),儘管這也是清理他人混亂(真正的Xtreme Programming風格)的一個機會。
- 有問題的程式碼出現在發現編碼規範之前,而且也沒有充足的理由去修改他們。
- 當代碼需要相容不支援編碼規範建議的老版本Python。
三、Code lay-out 程式碼佈局
3.1 Indentation 縮排
每一級縮排使用4個空格。
Googel:Tip
用4個空格來縮排程式碼
絕對不要用tab, 也不要tab和空格混用. 對於行連線的情況, 你應該要麼垂直對齊換行的元素(見 行長度 部分的示例), 或者使用4空格的懸掛式縮排(這時第一行不應該有引數):
續行應該與其包裹元素對齊,要麼使用圓括號、方括號和花括號內的隱式行連線來垂直對齊,要麼使用掛行縮排對齊3。當使用掛行縮排時,應該考慮到第一行不應該有引數,以及使用縮排以區分自己是續行。
推薦:
不推薦:
當if語句的條件部分長到需要換行寫的時候,注意可以在兩個字元關鍵字的連線處(比如if),增加一個空格,再增加一個左括號來創造一個4空格縮排的多行條件。這會與if語句內同樣使用4空格縮排的程式碼產生視覺衝突。PEP沒有明確指明要如何區分i發的條件程式碼和內嵌程式碼。可使用的選項包括但不限於下面幾種情況:
個人喜歡第二種。
(可以參考下面關於是否在二進位制運算子之前或之後截斷的討論)
在多行結構中的大括號/中括號/小括號的右括號可以與內容對齊單獨起一行作為最後一行的第一個字元,就像這樣:
或者也可以與多行結構的第一行第一個字元對齊,就像這樣:
個人喜歡上面一種。
3.2 Tabs or Spaces 製表符還是空格
- 空格是首選的縮排方式。
- 製表符只能用於與同樣使用製表符縮排的程式碼保持一致。
- Python3不允許同時使用空格和製表符的縮排。
- 混合使用製表符和空格縮排的Python2程式碼應該統一轉成空格。
- 當在命令列加入-t選項執行Python2時,它會發出關於非法混用製表符與空格的警告。當使用–tt時,這些警告會變成錯誤。強烈建議使用這樣的引數。
3.3 Maximum Line Length 行的最大長度
所有行限制的最大字元數為79。可以使用()解決字串的問題:
沒有結構化限制的大塊文字(文件字元或者註釋),每行的最大字元數限制在72。
限制編輯器視窗寬度可以使多個檔案並行開啟,並且在使用程式碼檢查工具(在相鄰列中顯示這兩個版本)時工作得很好。
大多數工具中的預設封裝破壞了程式碼的視覺化結構,使程式碼更難以理解。避免使用編輯器中預設配置的80視窗寬度,即使工具在幫你折行時在最後一列放了一個標記符。某些基於Web的工具可能根本不提供動態折行。
一些團隊更喜歡較長的行寬。如果程式碼主要由一個團隊維護,那這個問題就能達成一致,可以把行長度從80增加到100個字元(更有效的做法是將行最大長度增加到99個字元),前提是註釋和文件字串依然已72字元折行。
Python標準庫比較保守,需要將行寬限制在79個字元(文件/註釋限制在72)。
較長的程式碼行選擇Python在小括號,中括號以及大括號中的隱式續行方式。通過小括號內表示式的換行方式將長串折成多行。這種方式應該優先使用,而不是使用反斜槓續行。
反斜槓有時依然很有用。比如,比較長的,多個with狀態語句,不能使用隱式續行,所以反斜槓是可以接受的:
(請參閱前面關於多行if-語句的討論,以獲得關於這種多行with-語句縮排的進一步想法。)
另一種類似情況是使用assert語句。
確保在續行進行適當的縮排。
google編寫規範:
例外:
- 長的匯入模組語句
- 註釋裡的URL
不要使用反斜槓連線行.
3.4 Should a line break before or after a binary operator 在二元運算子之前應該換行嗎
幾十年來,推薦的風格是在二元運算子之後中斷。但是這會影響可讀性,原因有二:操作符一般分佈在螢幕上不同的列中,而且每個運算子被移到了運算元的上一行。下面例子這個情況就需要額外注意,那些變數是相加的,那些變數是相減的:
為了解決這種可讀性的問題,數學家和他們的出版商遵循了相反的約定。Donald Knuth在他的Computers and Typesetting系列中解釋了傳統規則:"儘管段落中的公式總是在二元運算子和關係之後中斷,顯示出來的公式總是要在二元運算子之前中斷"4。
遵循數學的傳統能產出更多可讀性高的程式碼:
在Python程式碼中,允許在二元運算子之前或之後中斷,只要本地的約定是一致的。對於新程式碼,建議使用Knuth的樣式。
3.5 Blank Lines 空行
- 頂層函式和類的定義,前後用兩個空行隔開。
- 類裡的方法定義用一個空行隔開。
- 相關的功能組可以用額外的空行(謹慎使用)隔開。一堆相關的單行程式碼之間的空白行可以省略(例如,一組虛擬實現 dummy implementations)。
- 在函式中使用空行來區分邏輯段(謹慎使用)。
- Python接受control-L(即^L)換頁符作為空格;許多工具把這些字元當作頁面分隔符,所以你可以在檔案中使用它們來分隔相關段落。請注意,一些編輯器和基於Web的程式碼閱讀器可能無法識別control-L為換頁,將在其位置顯示另一個字形。
3.6 Source File Encoding 原始檔編碼
Python核心釋出版本中的程式碼總是以UTF-8格式編碼(或者在Python2中用ASCII編碼)。
使用ASCII(在Python2中)或UTF-8(在Python3中)編碼的檔案不應具有編碼宣告。
在標準庫中,非預設的編碼應該只用於測試,或者當一個註釋或者文件字串需要提及一個包含內ASCII字元編碼的作者名字的時候;否則,使用\x,\u,\U , 或者 \N 進行轉義來包含非ASCII字元。
對於Python 3和更高版本,標準庫規定了以下策略(參見 PEP 3131):Python標準庫中的所有識別符號必須使用ASCII識別符號,並在可行的情況下使用英語單詞(在許多情況下,縮寫和技術術語是非英語的)。此外,字串文字和註釋也必須是ASCII。唯一的例外是(a)測試非ASCII特徵的測試用例,以及(b)作者的名稱。作者的名字如果不使用拉丁字母拼寫,必須提供一個拉丁字母的音譯。
鼓勵具有全球受眾的開放原始碼專案採取類似的政策。
3.7 Imports 匯入
匯入通常在分開的行,例如:
但是可以這樣:
匯入總是位於檔案的頂部,在模組註釋和文件字串之後,在模組的全域性變數與常量之前。
匯入應該按照以下順序分組:
- 標準庫匯入
- 相關第三方庫匯入
- 本地應用/庫特定匯入
你應該在每一組匯入之間加入空行。
推薦使用絕對路徑匯入,如果匯入系統沒有正確的配置(比如包裡的一個目錄在sys.path裡的路徑後),使用絕對路徑會更加可讀並且效能更好(至少能提供更好的錯誤資訊)
然而,顯示的指定相對匯入路徑是使用絕對路徑的一個可接受的替代方案,特別是在處理使用絕對路徑匯入不必要冗長的複雜包佈局時:
標準庫要避免使用複雜的包引入結構,而總是使用絕對路徑。
不應該使用隱式相對路徑匯入,並且在Python 3中刪除了它
當從一個包含類的模組中匯入類時,常常這麼寫:
如果上述的寫法導致名字的衝突,那麼這麼寫:
然後使用"myclass.MyClass"和"foo.bar.yourclass.YourClass"。
避免萬用字元的匯入(from import *),因為這樣做會不知道名稱空間中存在哪些名字,會使得讀取介面和許多自動化工具之間產生混淆。對於萬用字元的匯入,有一個防禦性的做法,即將內部介面重新發布為公共API的一部分(例如,用可選加速器模組的定義覆蓋純Python實現的介面,以及重寫那些事先不知道的定義)。
當以這種方式重新發布名稱時,以下關於公共和內部介面的準則仍然適用。
3.8 Module level dunder names 模組級的呆名
像__all__ , __author__ , __version__ 等這樣的模組級"呆名"(也就是名字裡有兩個字首下劃線和兩個字尾下劃線),應該放在文件字串的後面,以及除from __future__ 之外的import表示式前面。Python要求將來在模組中的匯入,必須出現在除文件字串之外的其他程式碼之前。
比如:
3.9 String Quotes 字串引號
在Python中,單引號和雙引號字串是相同的。PEP不會為這個給出建議。選擇一條規則並堅持使用下去(好多人使用雙引號)。當一個字串中包含單引號或者雙引號字元的時候,使用和最外層不同的符號來避免使用反斜槓,從而提高可讀性。
對於三引號字串,總是使用雙引號字元來與PEP 257中的文件字串約定保持一致。
四、Whitespace in Expressions and Statements 表示式和語句中的空格
4.1 Pet Peeves 不能忍受的事情
在下列情況下,避免使用無關的空格:
-
緊跟在小括號,中括號或者大括號後。
-
緊貼在逗號、分號或者冒號之前。
- 然而,冒號在切片中就像二元運算子,在兩邊應該有相同數量的空格(把它當做優先順序最低的操作符)。在擴充套件的切片操作中,所有的冒號必須有相同的間距。例外情況:當一個切片引數被省略時,空格就被省略了。
推薦: -
不推薦:
-
緊貼在函式引數的左括號之前。
-
緊貼索引或者切片的左括號之前。
-
為了和另一個賦值語句對齊,在賦值運算子附件加多個空格。
推薦:不推薦:
4.2 Other Recommendations 其他建議
- 避免在尾部新增空格。因為尾部的空格通常都看不見,會產生混亂:比如,一個反斜槓後面跟一個空格的換行符,不算續行標記。有些編輯器不會保留尾空格,並且很多專案(像CPython)在pre-commit的掛鉤呼叫中會過濾掉尾空格。
- 總是在二元運算子兩邊加一個空格:賦值(=),增量賦值(+=,-=),比較(==,<,>,!=,<>,<=,>=,in,not,in,is,is not),布林(and, or, not)。
-
如果使用具有不同優先順序的運算子,請考慮在具有最低優先順序的運算子周圍新增空格。有時需要通過自己來判斷;但是,不要使用一個以上的空格,並且在二元運算子的兩邊使用相同數量的空格。
推薦
不推薦
-
在制定關鍵字引數或者預設引數值的時候,不要在=附近加上空格。
推薦:
不推薦:
-
功能型註釋應該使用冒號的一般性規則,並且在使用->的時候要在兩邊加空格。(參考下面的功能註釋得到能夠多資訊)
推薦:
不推薦:
-
當給有型別備註的引數賦值的時候,在=兩邊新增空格(僅針對那種有型別備註和預設值的引數)。
推薦:
不推薦:
-
複合語句(同一行中的多個語句)通常是不允許的。
推薦:
最好別這樣:
-
雖然有時候將小的程式碼塊和 if/for/while 放在同一行沒什麼問題,多行語句塊的情況不要這樣用,同樣也要避免程式碼行太長!
最好別這樣:
-
絕對別這樣:
五、Comments 註釋
與程式碼相矛盾的註釋比沒有註釋還糟,當代碼更改時,優先更新對應的註釋!
註釋應該是完整的句子。如果一個註釋是一個短語或句子,它的第一個單詞應該大寫,除非它是以小寫字母開頭的識別符號(永遠不要改變識別符號的大小寫!)。
如果註釋很短,結尾的句號可以省略。塊註釋一般由完整句子的一個或多個段落組成,並且每句話結束有個句號。
在句尾結束的時候應該使用兩個空格。
當用英文書寫時,遵循Strunk and White (譯註:《Strunk and White, The Elements of Style》)的書寫風格。
在非英語國家的Python程式設計師,請使用英文寫註釋,除非你120%的確信你的程式碼不會被使用其他語言的人閱讀。
Google規範:
1.1 函式
一個函式必須要有文件字串, 除非它滿足以下條件:
- 外部不可見
- 非常短小
- 簡單明瞭
文件字串應該包含函式做什麼, 以及輸入和輸出的詳細描述. 通常, 不應該描述"怎麼做", 除非是一些複雜的演算法. 文件字串應該提供足夠的資訊, 當別人編寫程式碼呼叫該函式時, 他不需要看一行程式碼, 只要看文件字串就可以了. 對於複雜的程式碼, 在程式碼旁邊加註釋會比使用文件字串更有意義.
Args:
列出每個引數的名字, 並在名字後使用一個冒號和一個空格, 分隔對該引數的描述.如果描述太長超過了單行80字元,使用2或者4個空格的懸掛縮排(與檔案其他部分保持一致). 描述應該包括所需的型別和含義. 如果一個函式接受*foo(可變長度引數列表)或者**bar (任意關鍵字引數), 應該詳細列出*foo和**bar.
Returns: (或者 Yields: 用於生成器)
描述返回值的型別和語義. 如果函式返回None, 這一部分可以省略.
Raises:
列出與介面有關的所有異常.
1.2 類
類應該在其定義下有一個用於描述該類的文件字串. 如果你的類有公共屬性(Attributes), 那麼文件中應該有一個屬性(Attributes)段. 並且應該遵守和函式引數相同的格式.
1.3 塊註釋和行註釋
最需要寫註釋的是程式碼中那些技巧性的部分. 如果你在下次 程式碼審查 的時候必須解釋一下, 那麼你應該現在就給它寫註釋. 對於複雜的操作, 應該在其操作開始前寫上若干行註釋. 對於不是一目瞭然的程式碼, 應在其行尾添加註釋.
為了提高可讀性, 註釋應該至少離開程式碼2個空格.
另一方面, 絕不要描述程式碼. 假設閱讀程式碼的人比你更懂Python, 他只是不知道你的程式碼要做什麼.
5.1 Block Comments 塊註釋
塊註釋通常適用於跟隨它們的某些(或全部)程式碼,並縮排到與程式碼相同的級別。塊註釋的每一行開頭使用一個#和一個空格(除非塊註釋內部縮排文字)。
塊註釋內部的段落通過只有一個#的空行分隔。
5.2 Inline Comments 行內註釋
有節制地使用行內註釋。
行內註釋是與程式碼語句同行的註釋。行內註釋和程式碼至少要有兩個空格分隔。註釋由#和一個空格開始。
事實上,如果狀態明顯的話,行內註釋是不必要的,反而會分散注意力。比如說下面這樣就不需要:
5.3 Documentation Strings 文件字串
編寫好的文件說明(也叫"docstrings")的約定在PEP 257中永恆不變。
- 要為所有的公共模組,函式,類以及方法編寫文件說明。非公共的方法沒有必要,但是應該有一個描述方法具體作用的註釋。這個註釋應該在def那一行之後。
- PEP 257 描述了寫出好的文件說明相關的約定。特別需要注意的是,多行文件說明使用的結尾三引號應該自成一行,例如:
- 對於單行的文件說明,尾部的三引號應該和文件在同一行。
六、Naming Conventions 命名規範
Python庫的命名規範很亂,從來沒能做到完全一致。但是目前有一些推薦的命名標準。新的模組和包(包括第三方框架)應該用這套標準,但當一個已有庫採用了不同的風格,推薦保持內部一致性。
6.1 Overriding Principle 最重要的原則
那些暴露給使用者的API介面的命名,應該遵循反映使用場景而不是實現的原則。
6.2 Descriptive Naming Styles 描述命名風格
有許多不同的命名風格。這裡能夠幫助大家識別正在使用什麼樣的命名風格,而不考慮他們為什麼使用。
以下是常見的命名方式:
- b(單個小寫字母)
- B(單個大寫字母)
- lowercase 小寫字母
- lower_case_with_underscores 使用下劃線分隔的小寫字母
- UPPERCASE 大寫字母
- UPPER_CASE_WITH_UNDERSCORES 使用下劃線分隔的大寫字母
- CapitalizedWords(或者叫 CapWords,或者叫CamelCase 駝峰命名法 —— 這麼命名是因為字母看上去有起伏的外觀5)。有時候也被稱為StudlyCaps。
- 注意:當在首字母大寫的風格中用到縮寫時,所有縮寫的字母用大寫,因此,HTTPServerError 比 HttpServerError 好。
- mixedCase(不同於首字母大寫,第一個單詞的首字母小寫)
- Capitalized_Words_With_Underscores(巨醜無比!)
也有用唯一的短字首把相關命名組織在一起的方法。這在Python中不常用,但還是提一下。比如,os.stat()函式中包含類似以st_mode,st_size,st_mtime這種傳統命名方式命名的變數。(這麼做是為了與 POSIX 系統的呼叫一致,以幫助程式設計師熟悉它。)
X11庫的所有公共函式都加了字首X。在Python裡面沒必要這麼做,因為屬性和方法在呼叫的時候都會用類名做字首,函式名用模組名做字首。
另外,下面這種用字首或結尾下劃線的特殊格式是被認可的(通常和一些約定相結合):
- _single_leading_underscore:(單下劃線開頭)弱"內部使用"指示器。比如 from M import * 是不會匯入以下劃線開始的物件的。
- single_trailing_underscore_:(單下劃線結尾)這是避免和Python內部關鍵詞衝突的一種約定,比如:Tkinter.Toplevel(master, class_='ClassName')
- __double_leading_underscore:(雙下劃線開頭)當這樣命名一個類的屬性時,呼叫它的時候名字會做矯正(在類FooBar中,__boo變成了_FooBar__boo;見下文)。
- __double_leading_and_trailing_underscore__:(雙下劃線開頭,雙下劃線結尾)"magic"物件或者存在於使用者控制的名稱空間內的屬性,例如:__init__,__import__或者__file__。除了作為文件之外,永遠不要命這樣的名。
6.3 Prescriptive Naming Conventions 約定俗成命名約定
-
Names to Avoid 應避免的名字
永遠不要使用字母'l'(小寫的L),'O'(大寫的O),或者'I'(大寫的I)作為單字元變數名。
在有些字型裡,這些字元無法和數字0和1區分,如果想用'l',用'L'代替。
-
Package and Module Names 包名和模組名
模組應該用簡短全小寫的名字,如果為了提升可讀性,下劃線也是可以用的。Python包名也應該使用簡短全小寫的名字,但不建議用下劃線。
當使用C或者C++編寫了一個依賴於提供高階(更面向物件)介面的Python模組的擴充套件模組,這個C/C++模組需要一個下劃線字首(例如:_socket)
-
Class Names 類名
類名一般使用首字母大寫的約定。
在介面被文件化並且主要被用於呼叫的情況下,可以使用函式的命名風格代替。
注意,對於內建的變數命名有一個單獨的約定:大部分內建變數是單個單詞(或者兩個單詞連線在一起),首字母大寫的命名法只用於異常名或者內部的常量。
-
Exception Names 異常名
因為異常一般都是類,所有類的命名方法在這裡也適用。然而,你需要在異常名後面加上"Error"字尾(如果異常確實是一個錯誤)。
-
Global Variable Names 全域性變數名
(我們希望這一類變數只在模組內部使用。)約定和函式命名規則一樣。
通過 from M import * 匯入的模組應該使用all機制去防止內部的介面對外暴露,或者使用在全域性變數前加下劃線的方式(表明這些全域性變數是模組內非公有)。
-
Function Names 函式名
函式名應該小寫,如果想提高可讀性可以用下劃線分隔。
大小寫混合僅在為了相容原來主要以大小寫混合風格的情況下使用(比如 threading.py),保持向後相容性。
-
Function and method arguments 函式和方法引數
始終要將 self 作為例項方法的的第一個引數。
始終要將 cls 作為類靜態方法的第一個引數。
如果函式的引數名和已有的關鍵詞衝突,在最後加單一下劃線比縮寫或隨意拼寫更好。因此 class_ 比 clss 更好。(也許最好用同義詞來避免這種衝突)
-
Method Names and Instance Variables 方法名和例項變數
遵循這樣的函式命名規則:使用下劃線分隔小寫單詞以提高可讀性。
在非共有方法和例項變數前使用單下劃線。
通過雙下劃線字首觸發Python的命名轉換規則來避免和子類的命名衝突。
Python通過類名對這些命名進行轉換:如果類 Foo 有一個叫 __a 的成員變數, 它無法通過 Foo.__a 訪問。(執著的使用者可以通過 Foo._Foo__a 訪問。)一般來說,字首雙下劃線用來避免類中的屬性命名與子類衝突的情況。
注意:關於__names的用法存在爭論(見下文)。
-
Constants 常量
常量通常定義在模組級,通過下劃線分隔的全大寫字母命名。例如: MAX_OVERFLOW 和 TOTAL。
-
Designing for inheritance 繼承的設計
始終要考慮到一個類的方法和例項變數(統稱:屬性)應該是共有還是非共有。如果存在疑問,那就選非共有;因為將一個非共有變數轉為共有比反過來更容易。
公共屬性是那些與類無關的客戶使用的屬性,並承諾避免向後不相容的更改。非共有屬性是那些不打算讓第三方使用的屬性;你不需要承諾非共有屬性不會被修改或被刪除。
我們不使用"私有(private)"這個說法,是因為在Python中目前還沒有真正的私有屬性(為了避免大量不必要的常規工作)。
另一種屬性作為子類API的一部分(在其他語言中通常被稱為"protected")。有些類是專為繼承設計的,用來擴充套件或者修改類的一部分行為。當設計這樣的類時,要謹慎決定哪些屬性時公開的,哪些是作為子類的API,哪些只能在基類中使用。
貫徹這樣的思想,一下是一些讓程式碼Pythonic的準則:
- 公共屬性不應該有字首下劃線。
- 如果公共屬性名和關鍵字衝突,在屬性名之後增加一個下劃線。這比縮寫和隨意拼寫好很多。(然而,儘管有這樣的規則,在作為引數或者變數時,'cls'是表示'類'最好的選擇,特別是作為類方法的第一個引數。)
注意1:參考之前的類方法引數命名建議 - 對於單一的共有屬性資料,最好直接對外暴露它的變數名,而不是通過負責的 存取器(accessor)/突變(mutator) 方法。請記住,如果你發現一個簡單的屬性需要成長為一個功能行為,那麼Python為這種將來會出現的擴充套件提供了一個簡單的途徑。在這種情況下,使用屬性去隱藏屬性資料訪問背後的邏輯。
注意1:屬性只在new-style類中起作用。
注意2:儘管功能方法對於類似快取的負面影響比較小,但還是要儘量避免。
注意3:屬性標記會讓呼叫者認為開銷(相當的)小,避免用屬性做開銷大的計算。 - 如果你的類打算用來繼承的話,並且這個類裡有不希望子類使用的屬性,就要考慮使用雙下劃線字首並且沒有後綴下劃線的命名方式。這會呼叫Python的命名轉換演算法,將類的名字加入到屬性名裡。這樣做可以幫助避免在子類中不小心包含了相同的屬性名而產生的衝突。
注意1:只有類名才會整合進屬性名,如果子類的屬性名和類名和父類都相同,那麼你還是會有命名衝突的問題。
注意2:命名轉換會在某些場景使用起來不太方便,例如除錯,__getattr__()。然而命名轉換的演算法有很好的文件說明並且很好操作。
注意3:不是所有人都喜歡命名轉換。儘量避免意外的名字衝突和潛在的高階呼叫。
本人計劃:變數用駝峰,函式有下劃線名稱小寫連結。
6.4 Public and internal interfaces 公共和內部的介面
任何向後相容保證只適用於公共介面,因此,使用者清晰地區分公共介面和內部介面非常重要。
文件化的介面被認為是公開的,除非文件明確宣告它們是臨時或內部介面,不受通常的向後相容性保證。所有未記錄的介面都應該是內部的。
為了更好地支援內省(introspection),模組應該使用__all__屬性顯式地在它們的公共API中宣告名稱。將__all__設定為空列表表示模組沒有公共API。
即使通過__all__設定過,內部介面(包,模組,類,方法,屬性或其他名字)依然需要單個下劃線字首。
如果一個名稱空間(包,模組,類)被認為是內部的,那麼包含它的介面也應該被認為是內部的。
匯入的名稱應該始終被視作是一個實現的細節。其他模組必須不能間接訪問這樣的名稱,除非它是包含它的模組中有明確的文件說明的API,例如 os.path 或者是一個包裡從子模組公開函式介面的 __init__ 模組。
七、Programming Recommendations 程式設計建議
- 程式碼應該用不損害其他Python實現的方式去編寫(PyPy,Jython,IronPython,Cython,Psyco 等)。
比如,不要依賴於在CPython中高效的內建字元連線語句 a += b 或者 a = a + b。這種優化甚至在CPython中都是脆弱的(它只適用於某些型別)並且沒有出現在不使用引用計數的實現中。在效能要求比較高的庫中,可以種 ".join() 代替。這可以確保字元關聯在不同的實現中都可以以線性時間發生。 - 和像None這樣的單例物件進行比較的時候應該始終用 is 或者 is not,永遠不要用等號運算子。
另外,如果你在寫 if x 的時候,請注意你是否表達的意思是 if x is not None。舉個例子,當測試一個預設值為None的變數或者引數是否被設定為其他值的時候。這個其他值應該是在上下文中能成為bool型別false的值。 -
使用 is not 運算子,而不是 not … is 。雖然這兩種表示式在功能上完全相同,但前者更易於閱讀,所以優先考慮。
推薦:不推薦:
- 當使用富比較(rich comparisons,一種複雜的物件間比較的新機制,允許返回值不為-1,0,1)實現排序操作的時候,最好實現全部的六個操作符(__eq__, __ne__, __lt__, __gt__, __ge__)而不是依靠其他的程式碼去實現特定的比較。
為了最大程度減少這一過程的開銷, functools.total_ordering() 修飾符提供了用於生成缺少的比較方法的工具。
PEP 207 指出Python實現了反射機制。因此,解析器會將 y > x 轉變為 x < y,將 y >= x 轉變為 x <= y,也會轉換x == y 和 x != y的引數。sort() 和 min()方法確保使用<操作符,max()使用>操作符。然而,最好還是實現全部六個操作符,以免在其他地方出現衝突。 -
始終使用def表示式,而不是通過賦值語句將lambda表示式繫結到一個變數上。
推薦:不推薦:
第一個形式意味著生成的函式物件的名稱是"f"而不是泛型"< lambda >"。這在回溯和字串顯示的時候更有用。賦值語句的使用消除了lambda表示式優於顯式def表示式的唯一優勢(即lambda表示式可以內嵌到更大的表示式中)。
- 從Exception繼承異常,而不是BaseException。直接繼承BaseException的異常適用於幾乎不用來捕捉的異常。
設計異常的等級,要基於撲捉異常程式碼的需要,而不是異常丟擲的位置。以程式設計的方式去回答"出了什麼問題?",而不是隻是確認"出現了問題"(內建異常結構的例子參考 PEP 3151 )
類的命名規範適用於這裡,但是你需要新增一個"Error"的字尾到你的異常類,如果異常是一個Error的話。非本地流控制或者其他形式的訊號的非錯誤異常不需要特殊的字尾。 - 適當地使用異常連結。在Python 3裡,為了不丟失原始的根源,可以顯式指定"raise X from Y"作為替代。
當故意替換一個內部異常時(Python 2 使用"raise X", Python 3.3 之後 使用 "raise X from None"),確保相關的細節轉移到新的異常中(比如把AttributeError轉為KeyError的時候保留屬性名,或者將原始異常資訊的文字內容內嵌到新的異常中)。 - 在Python 2中丟擲異常時,使用 rasie ValueError('message') 而不是用老的形式 raise ValueError, 'message'。
第二種形式在Python3 的語法中不合法
使用小括號,意味著當異常裡的引數非常長,或者包含字串格式化的時候,不需要使用換行符。 -
當捕獲到異常時,如果可以的話寫上具體的異常名,而不是隻用一個except: 塊。
比如說:
為了避免和原來基於逗號分隔的語法出現歧義,Python3只支援這一種語法。
當捕捉作業系統的錯誤時,推薦使用Python 3.3 中errno內定數值指定的異常等級。
另外,對於所有的 try/except 語句塊,在try語句中只填充必要的程式碼,這樣能避免掩蓋掉bug。
推薦:
不推薦:
- 當代碼片段區域性使用了某個資源的時候,使用with 表示式來確保這個資源使用完後被清理乾淨。用try/finally也可以。
-
無論何時獲取和釋放資源,都應該通過單獨的函式或方法呼叫上下文管理器。舉個例子:
推薦:不推薦:
- 從Exception繼承異常,而不是BaseException。直接繼承BaseException的異常適用於幾乎不用來捕捉的異常。
第二個例子沒有提供任何資訊去指明__enter__和__exit__方法在事務之後做出了關閉連線之外的其他事情。這種情況下,明確指明非常重要。
-
返回的語句保持一致。函式中的返回語句都應該返回一個表示式,或者都不返回。如果一個返回語句需要返回一個表示式,那麼在沒有值可以返回的情況下,需要用 return None 顯式指明,並且在函式的最後顯式指定一條返回語句(如果能跑到那的話)。
推薦:
不推薦:
- 使用字串方法代替字串模組。
字串方法總是更快,並且和unicode字串分享相同的API。如果需要相容Python2.0之前的版本可以不用考慮這個規則。 -
使用 ".startswith() 和 ".endswith() 代替通過字串切割的方法去檢查字首和字尾。
startswith()和endswith()更乾淨,出錯機率更小。比如: -
物件型別的比較應該用isinstance()而不是直接比較type。
當檢查一個物件是否為string型別時,記住,它也有可能是unicode string!在Python2中,str和unicode都有相同的基類:basestring,所以你可以這樣:
注意,在Python3中,unicode和basestring都不存在了(只有str)並且bytes型別的物件不再是string型別的一種(它是整數序列)
對於序列來說(strings,lists,tuples),可以使用空序列為false的情況。
- 書寫字串時不要依賴單詞結尾的空格,這樣的空格在視覺上難以區分,有些編輯器會自動去掉他們(比如 reindent.py (譯註:re indent 重新縮排))
-
不要用 == 去和True或者False比較:
7.1 Function Annotations 功能註釋
隨著PEP 484的引入,功能型註釋的風格規範有些變化。
- 為了向前相容,在Python3程式碼中的功能註釋應該使用 PEP 484的語法規則。(在前面的章節中對註釋有格式化的建議。)
- 不再鼓勵使用之前在PEP中推薦的實驗性樣式。
- 然而,在stdlib庫之外,在PEP 484中的實驗性規則是被鼓勵的。比如用PEP 484的樣式標記大型的第三方庫或者應用程式,回顧新增這些註釋是否簡單,並觀察是否增加了程式碼的可讀性。
- Python的標準庫程式碼應該保守使用這種註釋,但新的程式碼或者大型的重構可以使用這種註釋。
-
如果程式碼希望對功能註釋有不同的用途,建議在檔案的頂部增加一個這種形式的註釋:
#這會告訴檢查器忽略所有的註釋。(在 PEP 484中可以找到從型別檢查器禁用投訴的更細粒度的方法。)
- 像linters一樣,型別檢測器是可選的可獨立的工具。預設情況下,Python直譯器不應該因為型別檢查而發出任何訊息,也不應該基於註釋改變它們的行為。
- 不想使用型別檢測的使用者可以忽略他們。然而,第三方庫的使用者可能希望在這些庫上執行型別檢測。為此, PEP 484 建議使用存根檔案型別:.pyi檔案,這種檔案型別相比於.py檔案會被型別檢測器讀取。存根檔案可以和庫一起,或者通過typeshed repo6獨立釋出(通過庫作者的許可)
- 對於需要向後相容的程式碼,可以以註釋的形式新增功能型註釋。參見PEP 484的相關部分7。
八、其它
8.1 高效,快速方法
8.2 pycharm新增函式註釋:
方法一:
在python中當我們def一個函式之後,可以將滑鼠放置在定義的函式處,便會出現個小燈;小燈泡便會出現四行英文,如圖所示
好了,註釋新增完畢,我們可以在函式的呼叫處(將滑鼠放置函式呼叫處)用快捷鍵"ctrl+q"檢視
方法二:
自動生成函式註釋,包括引數和返回值。
使用方法,函式定義時,直接輸入三個雙引號後回車,例如:
def a(b,c):
"""
結果:
def a(b,c):
"""
:param b:
:param c:
:return:
"""
8.3 檢測工具
- PyCharm中自帶PEP8的檢測,點選右邊匡的黃色字型即可看到提示
- 用pep8工具檢測
- 用pylint工具檢測
8.4 開頭新增
通常開頭新增如下,提示使用的python路徑和編碼格式
#!/usr/bin/python
# -*- coding: UTF-8 -*-
參考
- 官網pep8規範(英文): https://www.python.org/dev/peps/pep-0008/ 對應中文:https://blog.csdn.net/ratsniper/article/details/78954852
- google編碼風格指南:https://zh-google-styleguide.readthedocs.io/en/latest/google-python-styleguide/python_language_rules/
- PEP 7, Style Guide for C Code, van Rossum
- Typeshed repo https://github.com/python/typeshed