#PEP 8 – Python編碼風格指南python
PEP: | 8 |
---|---|
Title: | Style Guide for Python Code |
Author: | Guido van Rossum , Barry Warsaw , Nick Coghlan |
Status: | Active |
Type: | Process |
Created: | 05-Jul-2001 |
Post-History: | 05-Jul-2001, 01-Aug-2013 |
這篇文檔說明了Python主要發行版中標準庫代碼所遵照的規範。對於Python的C語言實現中的編碼規範,請參考實現Python的C代碼風格指南PEP 7。git
這篇文檔和PEP 257(Docstring約定)都改編自Guido1最先的Python風格指南文章,而且添加了一些Barry的風格指南2中的內容。程序員
語言自身在發生着改變,隨着新的規範的出現和舊規範的過期,代碼風格也會隨着時間演變。github
不少項目都有本身的一套風格指南。若和本指南有任何衝突,應該優先考慮其項目相關的那套指南。web
Guido的一個重要觀點是代碼被讀的次數遠多於被寫的次數。這篇指南旨在提升代碼的可讀性,使浩瀚如煙的Python代碼風格能保持一致。正如PEP 20那首《Zen of Python》的小詩裏所說的:「可讀性很重要(Readability counts)」。算法
這本風格指南是關於一致性的。同風格指南保持一致性是重要的,可是同項目保持一致性更加劇要,同一個模塊和一個函數保持一致性則最爲重要。express
然而最最重要的是:要知道什麼時候去違反一致性,由於有時風格指南並不適用。當存有疑慮時,請自行作出最佳判斷。請參考別的例子去作出最好的決定。而且不要猶豫,儘管提問。編程
特別的:千萬不要爲了遵照這篇PEP而破壞向後兼容性!api
若是有如下藉口,則能夠忽略這份風格指南:緩存
當採用風格指南時會讓代碼更難讀,甚至對於習慣閱讀遵循這篇PEP的代碼的人來講也是如此。
須要和周圍的代碼保持一致性,但這些代碼違反了指南中的風格(但是時歷史緣由形成的)——儘管這可能也是一個收拾別人爛攤子的機會(進入真正的極限編程狀態)。
如果有問題的某段代碼早於引入指南的時間,那麼沒有必要去修改這段代碼。
代碼須要和更舊版本的Python保持兼容,而舊版本的Python不支持風格指南所推薦的特性。
每一個縮進級別採用4個空格。
連續行所包裝的元素應該要麼採用Python隱式續行,即垂直對齊於圓括號、方括號和花括號,要麼採用懸掛縮進(hanging indent)4。採用懸掛縮進時需考慮如下兩點:第一行不該該包括參數,而且在續行中須要再縮進一級以便清楚表示。
正確的例子:
# 同開始分界符(左括號)對齊 foo = long_function_name(var_one, var_two, var_three, var_four) # 續行多縮進一級以同其餘代碼區別 def long_function_name( var_one, var_two, var_three, var_four): print(var_one) # 懸掛縮進須要多縮進一級 foo = long_function_name( var_one, var_two, var_three, var_four)
錯誤的例子:
# 採用垂直對齊時第一行不該該有參數 foo = long_function_name(var_one, var_two, var_three, var_four) # 續行並無被區分開,所以須要再縮進一級 def long_function_name( var_one, var_two, var_three, var_four): print(var_one)
對於續行來講,4空格的規則能夠不遵照。
一樣可行的例子:
# 懸掛縮進能夠不採用4空格的縮進方法。 foo = long_function_name( var_one, var_two, var_three, var_four)
若是if
語句太長,須要用多行書寫,2個字符(例如,if
)加上一個空格和一個左括號恰好是4空格的縮進,但這對多行條件語句的續行是沒用的。由於這會和if
語句中嵌套的其餘的縮進的語句產生視覺上的衝突。這份PEP中並無作出明確的說明應該怎樣來區分條件語句和if
語句中所嵌套的語句。如下幾種方法都是可行的,但不只僅只限於這幾種方法:
# 不採用額外縮進 if (this_is_one_thing and that_is_another_thing): do_something() # 增長一行註釋,在編輯器中顯示時能有所區分 # supporting syntax highlighting. if (this_is_one_thing and that_is_another_thing): # Since both conditions are true, we can frobnicate. do_something() # 在條件語句的續行增長一級縮進 if (this_is_one_thing and that_is_another_thing): do_something()
多行結束右圓/方/花括號能夠單獨一行書寫,和上一行的縮進對齊:
my_list = [ 1, 2, 3, 4, 5, 6, ] result = some_function_that_takes_arguments( 'a', 'b', 'c', 'd', 'e', 'f', )
也能夠和多行開始的第一行的第一個字符對齊:
my_list = [ 1, 2, 3, 4, 5, 6, ] result = some_function_that_takes_arguments( 'a', 'b', 'c', 'd', 'e', 'f', )
###Tab仍是空格?(Tab Or Space?)
推薦使用空格來進行縮進。
Tab應該只在現有代碼已經使用tab進行縮進的狀況下使用,以便和現有代碼保持一致。
Python 3不容許tab和空格混合使用。
Python 2的代碼如有tab和空格混合使用的狀況,應該把tab所有轉換爲只有空格。
當使用命令行運行Python 2時,使用-t
選項,會出現非法混用tab和空格的警告。當使用-tt
選項時,這些警告會變成錯誤。強烈推薦使用這些選項!
將全部行都限制在79個字符長度之內。
對於連續大段的文字(好比文檔字符串(docstring)或註釋),其結構上的限制更少,這些行應該被限制在72個字符長度內。
限制編輯器的窗口寬度能讓好幾個文件同時打開在屏幕上顯示,在使用代碼評審(code review)工具時在兩個相鄰窗口顯示兩個版本的代碼效果很好。
不少工具的默認自動換行會破壞代碼的結構,使代碼更難以理解。在窗口大小設置爲80個字符的編輯器中,即便在換行時編輯器可能會在最後一列放置一個記號,爲避免自動換行也須要限制每行字符長度。一些基於web的工具可能根本沒有自動換行的功能。
一些團隊會強烈但願行長度比79個字符更長。當代碼僅僅只由一個團隊維護時,能夠達成一致讓行長度增長到80到100字符(實際上最大行長是99字符),註釋和文檔字符串仍然是以72字符換行。
Python標準庫比較傳統,將行長限制在79個字符之內(文檔字符串/註釋爲72個字符)。
一種推薦的換行方式是利用Python圓括號、方括號和花括號中的隱式續行。長行能夠經過在括號內換行來分紅多行。應該最好加上反斜槓來區別續行。
有時續行只能使用反斜槓才。例如,較長的多個with
語句不能採用隱式續行,只能接受反斜槓表示換行:
with open('/path/to/some/file/you/want/to/read') as file_1, \ open('/path/to/some/file/being/written', 'w') as file_2: file_2.write(file_1.read())
(參照前面關於 多行if語句的討論來進一步考慮這裏with
語句的縮進。)
另外一個這樣的例子是assert
語句。
要確保續行的縮進適當。
長期以來一直推薦的風格是在二元運算符以後換行。可是這樣會影響代碼可讀性,包括兩個方面:一是運算符會分散在屏幕上的不一樣列上,二是每一個運算符會留在前一行並遠離操做數。因此,閱讀代碼的時候眼睛必須作更多的工做來肯定哪些操做數被加,哪些操做數被減:
# 錯誤的例子:運算符遠離操做數 income = (gross_wages + taxable_interest + (dividends - qualified_dividends) - ira_deduction - student_loan_interest)
爲了解決這個可讀性問題,數學家及其出版商遵循相反的規定。Donald Knuth在他的「電腦和排版」系列中解釋了傳統的規則:「儘管在段落中的公式老是在二元運算符以後換行,但顯示公式時老是在二元運算符以前換行」5。
# 正確的例子:更容易匹配運算符與操做數 income = (gross_wages + taxable_interest + (dividends - qualified_dividends) - ira_deduction - student_loan_interest)
在Python代碼中,只要在統一項目中約定一致,就能夠在二元運算符以前或以後換行。對於新編寫的代碼,建議使用Knuth的風格。
使用2個空行來分隔最外層的函數(function)和類(class)定義。
使用1個空行來分隔類中的方法(method)定義。
可使用額外的空行(儘可能少)來分隔一組相關的函數。在一系列相關的僅佔一行的函數之間,空行也能夠被省略(好比一組虛函數定義)。
在函數內使用空行(儘可能少)使代碼邏輯更清晰。
Python支持control-L(如:^L)換頁符做爲空格;許多工具將這些符號做爲分頁符,所以你可使用這些符號來分頁或者區分文件中的相關區域。注意,一些編輯器和基於web的代碼預覽器可能不會將control-L識別爲分頁符,而是顯示成其餘符號。
Python核心發行版中的代碼應該一直使用UTF-8(Python 2中使用ASCII)。
使用ASCII(Python 2)或者UTF-8(Python 3)的文件不該該添加編碼聲明。
在標準庫中,只有用做測試目的,或者註釋或文檔字符串須要說起做者名字而不得不使用非ASCII字符時,才能使用非默認的編碼。不然,在字符串文字中包括非ASCII數據時,推薦使用\x
, \u
, \U
或\N
等轉義符。
對於Python 3.0及其之後的版本中,標準庫遵循如下原則(參見PEP 3131):Python標準庫中的全部標識符都必須只採用ASCII編碼的標識符,在可行的條件下也應當使用英文詞(不少狀況下,使用的縮寫和技術術語詞都不是英文)。此外,字符串文字和註釋應該只包括ASCII編碼。只有兩種例外:
鼓勵具備全球受衆的開放源碼項目採用相似的原則。
Imports應該分行寫,而不是都寫在一行,例如:
# 分開寫 import os import sys # 不要像下面同樣寫在一行 import sys, os
這樣寫也是能夠的:
from subprocess import Popen, PIPE
Imports應該寫在代碼文件的開頭,位於模塊(module)註釋和文檔字符串(docstring)以後,模塊全局變量(globals)和常量(constants)聲明以前。
Imports應該按照下面的順序分組來寫:
不一樣組的imports以前用空格隔開。
推薦使用絕對(absolute)imports,由於這樣一般更易讀,在import系統沒有正確配置(好比中的路徑以sys.path
結束)的狀況下,也會有更好的表現(或者至少會給出錯誤信息):
import mypkg.sibling from mypkg import sibling from mypkg.sibling import example
然而,除了絕對imports,顯式的相對imports也是一種能夠接受的替代方式。特別是當處理複雜的包佈局(package layouts)時,採用絕對imports會顯得囉嗦。
from . import sibling from .sibling import example
標準庫代碼應當一直使用絕對imports,避免複雜的包佈局。
隱式的相對imports應該永不使用,而且Python 3中已經被去掉了。
當從一個包括類的模塊中import一個類時,一般能夠這樣寫:
from myclass import MyClass from foo.bar.yourclass import YourClass
若是和本地命名的拼寫產生了衝突,應當直接import模塊:
import myclass import foo.bar.yourclass
而後使用」myclass.MyClass」和」foo.bar.yourclass.YourClass」。
避免使用通配符imports(from <module> import *
),由於會形成在當前命名空間出現的命名含義不清晰,給讀者和許多自動化工具形成困擾。有一個能夠正當使用通配符import的情形,即將一個內部接口從新發布成公共API的一部分(好比,使用備選的加速模塊中的定義去覆蓋純Python實現的接口,預先沒法知曉具體哪些定義將被覆蓋)。
當使用這種方式從新發布命名時,指南後面關於公共和內部接口的部分仍然適用。
模塊中的「雙下滑線」(變量名以兩個下劃線開頭,兩個下劃線結尾)變量,好比__all__
,__author
,__version__
等,應該寫在文檔字符串(docstring)以後,除了form __future__
引用(imports)的任何其它類型的引用語句以前。Python要求模塊中__future__
的導入必須出如今除文檔字符串(docstring)以外的任何其餘代碼以前。
例如:
"""This is the example module. This module does stuff. """ from __future__ import barry_as_FLUFL __all__ = ['a', 'b', 'c'] __version__ = '0.1' __author__ = 'Cardinal Biggles' import os import sys
在Python中表示字符串時,無論用單引號仍是雙引號都是同樣的。可是不推薦將這兩種方式看做同樣而且混用。最好選擇一種規則並堅持使用。當字符串中包含單引號時,採用雙引號來表示字符串,反之也是同樣,這樣能夠避免使用反斜槓,代碼也更易讀。
對於三引號表示的字符串,使用雙引號字符來表示6,這樣能夠和PEP 257的文檔字符串(docstring)規則保持一致。
在下列情形中避免使用過多的空白:
方括號,圓括號和花括號以後:
#正確的例子: spam(ham[1], {eggs: 2}) #錯誤的例子: spam( ham[ 1 ], { eggs: 2 } )
逗號,分號或冒號以前:
#正確的例子: if x == 4: print x, y; x, y = y, x #錯誤的例子: if x == 4 : print x , y ; x , y = y , x
不過,在切片操做時,冒號和二元運算符是同樣的,應該在其左右兩邊保留相同數量的空格(就像對待優先級最低的運算符同樣)。在擴展切片操做中,全部冒號的左右兩邊空格數都應該相等。不過也有例外,當切片操做中的參數被省略時,應該也忽略空格。
#正確的例子: ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:] ham[lower:upper], ham[lower:upper:], ham[lower::step] ham[lower+offset : upper+offset] ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)] ham[lower + offset : upper + offset] #錯誤的例子: ham[lower + offset:upper + offset] ham[1: 9], ham[1 :9], ham[1:9 :3] ham[lower : : upper] ham[ : upper]
在調用函數時傳遞參數list的括號以前:
#正確的例子: spam(1) #錯誤的例子: pam (1)
在索引和切片操做的左括號以前:
#正確的例子: dct['key'] = lst[index] #錯誤的例子: dct ['key'] = lst [index]
賦值(或其餘)運算符周圍使用多個空格來和其餘語句對齊:
#正確的例子: x = 1 y = 2 long_variable = 3 #錯誤的例子: x = 1 y = 2 long_variable = 3
=
),增量賦值運算符(+=
, -=
etc.),比較運算符(==
, <
, >
, !=
, <>
, <=
, >=
, in
, not in
, is
, is not
),布爾運算符(and
, or
, not
)。若是使用了優先級不一樣的運算符,則在優先級較低的操做符周圍增長空白。請你自行判斷,不過永遠不要用超過1個空格,永遠保持二元運算符兩側的空白數量同樣。
#正確的例子: i = i + 1 submitted += 1 x = x*2 - 1 hypot2 = x*x + y*y c = (a+b) * (a-b) #錯誤的例子: i=i+1 submitted +=1 x = x * 2 - 1 hypot2 = x * x + y * y c = (a + b) * (a - b)
使用=
符號來表示關鍵字參數或參數默認值時,不要在其周圍使用空格。
#正確的例子: def complex(real, imag=0.0): return magic(r=real, i=imag) #錯誤的例子: def complex(real, imag = 0.0): return magic(r = real, i = imag)
函數註解中的:
也遵循通常的:
加空格的規則,在->
兩側各使用一個空格。(參見函數註解)
#正確的例子: def munge(input: AnyStr): ... def munge() -> AnyStr: ... #錯誤的例子: def munge(input:AnyStr): ... def munge()->PosInt: ...
在組合使用函數註解和參數默認值時,須要在=
兩側各使用一個空格(只有當這個參數既有函數註解,又有默認值的時候)。
#正確的例子: def munge(sep: AnyStr = None): ... def munge(input: AnyStr, sep: AnyStr = None, limit=1000): ... #錯誤的例子: def munge(input: AnyStr=None): ... def munge(input: AnyStr, limit = 1000): ...
複合語句(即將多行語句寫在一行)通常是不鼓勵使用的。
#正確的例子: if foo == 'blah': do_blah_thing() do_one() do_two() do_three() #最好不要這樣: if foo == 'blah': do_blah_thing() do_one(); do_two(); do_three()
有時也能夠將短小的if/for/while中的語句寫在一行,但對於有多個分句的語句永遠不要這樣作。也要避免將多行都寫在一塊兒。
#最好不要這樣: if foo == 'blah': do_blah_thing() for x in lst: total += x while t < 10: t = delay() #絕對不要這樣: if foo == 'blah': do_blah_thing() else: do_non_blah_thing() try: something() finally: cleanup() do_one(); do_two(); do_three(long, argument, list, like, this) if foo == 'blah': one(); two(); three()
末尾逗號一般是可選的,除非在定義單元素元組(tuple)時是必需的(並且在Python 2中,它們具備print語句的語義)。爲了清楚起見,建議使用括號(技術上來講是冗餘的)括起來。
#正確的例子: FILES = ('setup.cfg',) #也正確,但使人困惑: FILES = 'setup.cfg',
當使用版本控制系統時,在未來有可能擴展的列表末尾添加冗餘的逗號是有好處的。具體的作法是將每個元素寫在單獨的一行,並在行尾添加逗號,右括號單獨佔一行。可是,與有括號在同一行的末尾元素後面加逗號是沒有意義的(上述的單元素元組除外)。
#正確的例子: FILES = [ 'setup.cfg', 'tox.ini', ] initialize(FILES, error=True, ) #錯誤的例子: FILES = ['setup.cfg', 'tox.ini',] initialize(FILES, error=True,)
和代碼矛盾的註釋還不如沒有。當代碼有改動時,必定要優先更改註釋使其保持最新。
註釋應該是完整的多個句子。若是註釋是一個短語或一個句子,其首字母應該大寫,除非開頭是一個以小寫字母開頭的標識符(永遠不要更改標識符的大小寫)。
若是註釋很短,結束的句號能夠被忽略。塊註釋一般由一段或幾段完整的句子組成,每一個句子都應該以句號結束。
你應該在句尾的句號後再加上2個空格。
使用英文寫做,參考Strunk和White的《The Elements of Style》
來自非英語國家的Python程序員們,請使用英文來寫註釋,除非你120%肯定你的代碼永遠不會被不懂你所用語言的人閱讀到。
塊註釋通常寫在對應代碼以前,而且和對應代碼有一樣的縮進級別。塊註釋的每一行都應該以#
和一個空格開頭(除非該文本是在註釋內縮進對齊的)。
塊註釋中的段落應該用只含有單個#
的一行隔開。
儘可能少用行內註釋。
行內註釋是和代碼語句寫在一行內的註釋。行內註釋應該至少和代碼語句之間有兩個空格的間隔,而且以#
和一個空格開始。
行內註釋一般不是必要的,在代碼含義很明顯時甚至會讓人分心。請不要這樣作:
x = x + 1 # x自加
但這樣作是有用的:
x = x + 1 # 邊界補償
要知道如何寫出好的文檔字符串(docstring),請參考PEP 257。
全部的公共模塊,函數,類和方法都應該有文檔字符串。對於非公共方法,文檔字符串不是必要的,但你應該留有註釋說明該方法的功能,該註釋應當出如今def
的下一行。
PEP 257描述了好的文檔字符應該遵循的規則。其中最重要的是,多行文檔字符串以單行"""
結尾,不能有其餘字符,例如:
"""Return a foobang
Optional plotz says to frobnicate the bizbaz first.
"""
對於僅有一行的文檔字符串,結尾處的"""
應該也寫在這一行。
Python標準庫的命名約定有一些混亂,所以咱們永遠都沒法保持一致。但現在仍然存在一些推薦的命名標準。新的模塊和包(包括第三方框架)應該採用這些標準,但如果已經存在的包有另外一套風格的話,仍是應當與原有的風格保持內部一致。
對於用戶可見的公共部分API,其命名應當表達出功能用途而不是其具體的實現細節。
存在不少不一樣的命名風格,最好可以獨立地從命名對象的用途認出採用了哪一種命名風格。
一般區分如下命名樣式:
b
(單個小寫字母)B
(單個大寫字母)lowercase
(小寫)lower_case_with_underscores
(帶下劃線小寫)UPPERCASE
(大寫)UPPER_CASE_WITH_UNDERSCORES
(帶下劃線大寫)CapitalizedWords
(也叫作CapWords或者CamelCase – 由於單詞首字母大寫看起來很像駝峯)。也被稱做StudlyCaps。
注意:當CapWords裏包含縮寫時,將縮寫部分的字母都大寫。HTTPServerError
比HttpServerError
要好。
mixedCase
(注意:和CapitalizedWords不一樣在於其首字母小寫!)Capitalized_Words_With_Underscores
(這種風格超醜!)也有風格使用簡短惟一的前綴來表示一組相關的命名。這在Python中並不常見,但爲了完整起見這裏也捎帶提一下。好比,os.stat()
函數返回一個tuple,其中的元素名本來爲st_mode
,st-size
,st_mtime
等等。(這樣作是爲了強調和POSIX系統調用結構之間的關係,可讓程序員更熟悉。)
X11庫中的公共函數名都以X開頭。在Python中這樣的風格通常被認爲是沒必要要的,由於屬性和方法名以前已經有了對象名的前綴,而函數名前也有了模塊名的前綴。
此外,要區別如下劃線開始或結尾的特殊形式(能夠和其它的規則結合起來):
_single_leading_underscore
: 以單個下劃線開頭是」內部使用」的弱標誌。 好比, from M import *
不會import下劃線開頭的對象。
single_trailing_underscore_
: 以單個下劃線結尾用來避免和Python關鍵詞產生衝突,例如:
Tkinter.Toplevel(master, class_='ClassName')
__double_leading_underscore
: 以雙下劃線開頭的風格命名類屬性表示觸發命名修飾(在FooBar類中,__boo
命名會被修飾成_FooBar__boo
; 見下)。
__double_leading_and_trailing_underscore__
: 以雙下劃線開頭和結尾的命名風格表示「魔術」對象或屬性,存在於用戶控制的命名空間(user-controlled namespaces)裏(也就是說,這些命名已經存在,但一般須要用戶覆寫以實現用戶所須要的功能)。 好比, __init__
, __import__
或 __file__
。請依照文檔描述來使用這些命名,千萬不要本身發明。
不要使用字符’l’(L的小寫的字母),’O’(o大寫的字母),或者’I’(i的大寫的字母)來做爲單個字符的變量名。
在一些字體中,這些字符和數字1和0沒法區別開來。好比,當想使用’l’時,使用’L’代替。
標準庫中使用的標識符必須與ASCII兼容(參見PEP 3131中的policy這一節) 。
模塊命名應短小,且爲全小寫。若下劃線能提升可讀性,也能夠在模塊名中使用。Python包命名也應該短小,且爲全小寫,但不該使用下劃線。
當使用C或C++寫的擴展模塊有相應的Python模塊提供更高級的接口時(好比,更加面向對象),C/C++模塊名如下劃線開頭(例如,_sociket
)。
類命名應該使用駝峯(CapWords)的命名約定。
當接口已有文檔說明且主要是被用做調用時,也可使用函數的命名約定。
注意對於內建命名(builtin names)有一個特殊的約定:大部份內建名都是一個單詞(或者兩個一塊兒使用的單詞),駝峯(CapWords)的約定只對異常命名和內建常量使用。
PEP 484中引入的類型變量名稱一般應使用簡短的駝峯命名: T
,AnyStr
,Num
。 建議將後綴_co
或_contra
添加到用於聲明相應的協變(covariant)和逆變(contravariant)的行爲。例如:
from typing import TypeVar VT_co = TypeVar('VT_co', covariant=True) KT_contra = TypeVar('KT_contra', contravariant=True)
因爲異常實際上也是類,所以類命名約定也適用與異常。不一樣的是,若是異常其實是拋出錯誤的話,異常名前應該加上」Error」的前綴。
(在此以前,咱們先假定這些變量都僅在同一個模塊內使用。)這些約定一樣也適用於函數命名。
對於引用方式設計爲from M import *
的模塊,應該使用__all__
機制來避免import全局變量,或者採用下劃線前綴的舊約定來命名全局變量,從而代表這些變量是「模塊非公開的」。
函數命名應該都是小寫,必要時使用下劃線來提升可讀性。
只有當已有代碼風格已是混合大小寫時(好比threading.py),爲了保留向後兼容性才使用混合大小寫。
實例方法的第一參數永遠都是self
。
類方法的第一個參數永遠都是cls
。
在函數參數名和保留關鍵字衝突時,相對於使用縮寫或拼寫簡化,使用如下劃線結尾的命名通常更好。好比,class_
比clss
更好。(或許使用同義詞避免這樣的衝突是更好的方式。)
使用函數命名的規則:小寫單詞,必要時使用下劃線分開以提升可讀性。
僅對於非公開方法和變量命名在開頭使用一個下劃線。
避免和子類的命名衝突,使用兩個下劃線開頭來觸發Python的命名修飾機制。
Python類名的命名修飾規則:若是類Foo有一個屬性叫__a
,不能使用Foo.__a
的方式訪問該變量。(有用戶可能仍然堅持使用Foo._Foo__a
的方法訪問。)通常來講,兩個下劃線開頭的命名方法僅用於避免與設計爲子類的類中的屬性名衝突。
注意:關於__names的使用也有一些爭論(見下)。
常量一般是在模塊級別定義的,使用所有大寫並用下劃線將單詞分開。如:MAX_OVERFLOW
和TOTAL
。
記得永遠區別類的方法和實例變量(屬性)應該是公開的仍是非公開的。若是有疑慮的話,請選擇非公開的;由於以後將非公開屬性變爲公開屬性要容易些。
公開屬性是那些你但願和你定義的類無關的客戶來使用的,而且確保不會出現向後不兼容的問題。非公開屬性是那些不但願被第三方使用的部分,你能夠不用保證非公開屬性不會變化或被移除。
咱們在這裏沒有使用「私有(private)」這個詞,由於在Python裏沒有什麼屬性是真正私有的(這樣設計省略了大量沒必要要的工做)。
另外一類屬性屬於子類API的一部分(在其餘語言中常常被稱爲」protected」)。一些類是爲繼承設計的,要麼擴展要麼修改類的部分行爲。當設計這樣的類時,須要謹慎明確地決定哪些屬性是公開的,哪些屬於子類API,哪些真的只會被你的基類調用。
請記住以上幾點,下面是Python風格的指南:
公開屬性不該該有開頭下劃線。
若是公開屬性的名字和保留關鍵字有衝突,在你的屬性名尾部加上一個下劃線。這比採用縮寫和簡寫更好。(然而,和這條規則衝突的是,‘cls’對任何變量和參數來講都是一個更好地拼寫,由於你們都知道這表示class,特別是在類方法的第一個參數裏。)
注意 1:對於類方法,參考以前的參數命名建議。
對於簡單的公共數據屬性,最後僅公開屬性名字,不要公開復雜的調用或設值方法。請記住,若是你發現一個簡單的數據屬性須要增長功能行爲時,Python爲功能加強提供了一個簡單的途徑。這種狀況下,使用Properties
註解將功能實現隱藏在簡單數據屬性訪問語法以後。
注意 1:Properties
註解僅僅對新風格類有用。
注意 2:儘可能保證功能行爲沒有反作用,儘管緩存這種反作用看上去並無什麼大問題。
注意 3: 對計算量大的運算避免試用properties;屬性的註解會讓調用者相信訪問的運算量是相對較小的。
若是你的類將被子類繼承的話,你有一些屬性並不想讓子類訪問,考慮將他們命名爲兩個下劃線開頭而且結尾處沒有下劃線。這樣會觸發Python命名修飾算法,類名會被修飾添加到屬性名中。這樣能夠避免屬性命名衝突,以避免子類會不經意間包含相同的命名。
注意 1:注意命名修飾僅僅是簡單地將類名加入到修飾名中,因此若是子類有相同的類名合屬性名,你可能仍然會遇到命名衝突問題。
注意 2:命名修飾能夠有特定用途,好比在調試時,__getattr__()
比較不方便。然而命名修飾算法的能夠很好地記錄,而且容意手動執行。
注意 3:不是全部人都喜歡命名修飾。須要試着去平衡避免偶然命名衝突的需求和高級調用者使用的潛在可能性。
任何向後兼容性保證僅對公開接口適用。相應地,用戶可以清楚分辨公開接口和內部接口是很重要的。
文檔化的接口被認爲是公開的,除非文檔中明確申明瞭它們是臨時的或者內部接口,不保證向後兼容性。全部文檔中未提到的接口應該被認爲是內部的。
爲了更好審視公開接口和內部接口,模塊應該在__all
屬性中明確申明公開API是哪些。將__all__
設爲空list表示該模塊中沒有公開API。
即便正確設置了__all
屬性,內部接口(包,模塊,類,函數,屬性或其餘命名)也應該以一個下劃線開頭。
若是接口的任一一個命名空間(包,模塊或類)是內部的,那麼該接口也應該是內部的。
引用的命名應該永遠被認爲是實現細節。其餘模塊不該當依賴這些非直接訪問的引用命名,除非它們在文檔中明確地被寫爲模塊的API,例如os.path
或者包的__init__
模塊,那些從子模塊展示的功能。
代碼應該以不影響其餘Python實現(PyPy,Jython,IronPython,Cython,Psyco等)的方式編寫。
例如,不要依賴於 CPython 在字符串拼接時的優化實現,像這種語句形式a += b
和a = a + b
。即便是 CPython(僅對某些類型起做用) 這種優化也是脆弱的,不是在全部的實現中都不使用引用計數。在庫中性能敏感的部分,用''.join
形式來代替。這會確保在全部不一樣的實現中字符串拼接是線性時間的。
與單例做比較,像None
應該用is
或is not
,從不使用==
操做符。
一樣的,小心if x is not None
這樣的寫法,你是不知真的要判斷x
不是None
。例如,測試一個默認值爲None
的變量或參數是否設置成了其它值,其它值有多是某種特殊類型(如容器),這種特殊類型在邏輯運算時其值會被看成Flase
來看待。
用is not
操做符而不是not ... is
。雖然這兩個表達式是功能相同的,前一個是更可讀的,是首選。
推薦的寫法:
if foo is not None:
不推薦的寫法:
if not foo is None:
用富比較實現排序操做的時候,最好實現全部六個比較操做符( __eq__
、 __ne__
、 __lt__
, __le__
, __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
表達式的賦值語句。
推薦的寫法:
def f(x): return 2*x
不推薦的寫法:
f = lambda x: 2*x
第一個表單意味着生成的函數對象的名稱是’f’而不是通用的’<lambda>‘。一般這對異常追蹤和字符串表述是更有用的。使用賦值語句消除了使用lambda
表達式能夠提供,而一個顯式的def
語句不能提供的惟一好處,如,lambda
能鑲嵌在一個很長的表達式裏。
異常類應派生自Exception
而不是BaseException
。直接繼承BaseException
是爲Exception
保留的,從BaseException
繼承並捕獲異常這種作法幾乎老是錯的。
設計異常的層次結構,應基於那些可能出現異常的代碼,而不是引起異常的位置。編碼的時候,以回答「出了什麼問題?」爲目標,而不是僅僅指出「這裏出現了問題」(見 PEP 3151 一個內建異常結構層次的例子)。
類的命名約定適用於異常,若是異常類是一個錯誤,你應該給異常類加一個後綴Error
。用於非本地流程控制或者其餘形式的信號的非錯誤異常不須要一個特殊的後綴。
適當的使用異常鏈。在 Python 3 裏,應該使用raise X from Y
來指示顯式替換,而不會丟失原始的追溯。
當有意替換一個內部的異常時(在 Python 2 用raise X
,Python 3.3+ 用raise X from None
),請確保將相關詳細信息轉移到新異常中(例如,將KeyError
轉換爲AttributeError
時保留屬性名稱,或將原始異常的文本嵌入到新的異常消息中)。
在 Python 2 裏拋出異常時,用raise ValueError('message')
代替舊式的raise ValueError, 'message'
。
在 Python 3 以後的語法裏,舊式的異常拋出方式是非法的。
使用括號形式的異常意味着,當你傳給異常的參數過長或者包含字符串格式化時,你就不須要使用續行符了,這要感謝括號!
捕獲異常時,儘量使用明確的異常,而不是用一個空的except:
語句。
例如,用:
try: import platform_specific_module except ImportError: platform_specific_module = None
一個空的except:
語句將會捕獲到SystemExit
和KeyboardInterrupt
異常,很難區分程序的中斷究竟是Ctrl+C
仍是其餘問題引發的。若是你想捕獲程序的全部錯誤,使用except Exception:
(空except:
等同於except BaseException
)。
一個好的經驗是限制使用空except
語句,除了這兩種狀況:
raise
向上拋出異常。try .. finally
是處理這種狀況更好的方式。綁定異常給一個名字時,最好使用 Python 2.6 裏添加的明確的名字綁定語法:
try: process_data() except Exception as exc: raise DataProcessingFailedError(str(exc))
Python 3 只支持這種語法,避免與基於逗號的舊式語法產生二義性。
捕獲操做系統錯誤時,最好使用 Python 3.3 裏引進的明確的異常結構層次,而不是內省的errno
值。
另外,對於全部try
/ except
子句,將try
子句限制爲必需的絕對最小代碼量。一樣,這樣能夠避免屏蔽錯誤。
推薦的寫法:
try: value = collection[key] except KeyError: return key_not_found(key) else: return handle_value(value)
不推薦的寫法:
try: # Too broad! return handle_value(collection[key]) except KeyError: # Will also catch KeyError raised by handle_value() return key_not_found(key)
當某個資源僅被特定代碼段使用,用with
語句確保其在使用後被當即乾淨的清除了,try/finally
也是也接受的。
當它們作一些除了獲取和釋放資源以外的事的時候,上下文管理器應該經過單獨的函數或方法調用。例如:
推薦的寫法:
with conn.begin_transaction(): do_stuff_in_transaction(conn)
不推薦的寫法:
with conn: do_stuff_in_transaction(conn)
第二個例子沒有提供任何信息來代表__enter__
和__exit__
方法在完成一個事務後作了一些除了關閉鏈接之外的其它事。在這種狀況下明確是很重要的。
堅持使用return
語句。函數內的return
語句都應該返回一個表達式,或者None
。若是一個return
語句返回一個表達式,另外一個沒有返回值的應該用return None
清晰的說明,而且在一個函數的結尾應該明確使用一個return
語句(若是有返回值的話)。
推薦的寫法:
def foo(x): if x >= 0: return math.sqrt(x) else: return None def bar(x): if x < 0: return None return math.sqrt(x)
不推薦的寫法:
def foo(x): if x >= 0: return math.sqrt(x) def bar(x): if x < 0: return return math.sqrt(x)
用字符串方法代替字符串模塊。
字符串方法老是快得多,而且與unicode字符串共享相同的API。若是須要與2.0如下的Python的向後兼容,則覆蓋此規則。
用''.startswith()
和''.endswith()
代替字符串切片來檢查前綴和後綴。
startswith()
和endswith()
是更簡潔的,不容易出錯的。例如:
#推薦的寫法: if foo.startswith('bar'): #不推薦的寫法: if foo[:3] == 'bar':
對象類型的比較應該始終使用isinstance()
而不是直接比較。
#推薦的寫法: if isinstance(obj, int): #不推薦的寫法: if type(obj) is type(1):
當比較一個對象是否是字符串時,記住它有可能也是一個 unicode 字符串!在 Python 2 裏面,str
和unicode
有一個公共的基類叫basestring
,所以你能夠這樣作:
if isinstance(obj, basestring):
注意,在 Python 3 裏面,unicode
和basestring
已經不存在了(只有str
),byte
對象再也不是字符串的一種(被一個整數序列替代)。
對於序列(字符串、列表、元組)來講,空的序列爲False
:
正確的寫法:
if not seq: if seq:
錯誤的寫法:
if len(seq): if not len(seq):
不要讓字符串對尾隨的空格有依賴。這樣的尾隨空格是視覺上沒法區分的,一些編輯器(或者,reindent.py)會將其裁剪掉。
不要用==
比較True
和False
。
#推薦的寫法: if greeting: #不推薦的寫法: if greeting == True: #更加不推薦的寫法: if greeting is True:
隨着PEP 484被正式接受,函數註釋的樣式規則已經改變。
爲了向前兼容,Python 3代碼中的函數註釋最好使用PEP 484語法。(上一節中的有一些關於註解格式的建議。)
建議再也不使用在此文檔早期版本中描述的試驗性質的註解樣式。
然而,在標準庫(stdlib)以外,如今鼓勵在PEP 484的規則範圍內的實驗。例如,使用PEP 484樣式類型的註解標記大型第三方庫或應用程序,評估添加這些註解的方式是否簡便,並觀察其存在是否增長了代碼可讀性。
Python標準庫在採用這些註解時應持謹慎態度,可是當編寫新代碼或進行大的重構時,容許使用。
若是但願不按照函數註解的方式來使用函數,能夠在文件頭部添加如下注釋:
# type: ignore
這會告訴類型檢查器忽略全部註解。 (在PEP 484中能夠找到更細緻的方式來控制類型檢查器的行爲。)
像代碼掃描工具同樣(linters),類型檢查器是可選的,單獨的工具。默認狀況下,Python解釋器不該該因爲類型檢查而發出任何消息,而且不該該根據註釋來改變它們的行爲。
不想使用類型檢查器的用戶能夠忽略它們。可是,預計第三方庫軟件包的用戶可能但願在這些軟件包上運行類型檢查器。爲此,PEP 484 建議使用存根文件:.pyi文件,類型檢查器優先於相應的.py文件讀取這個文件。存根文件能夠與庫一塊兒分發,也能夠單獨地(在庫做者許可的狀況下)經過Typeshed repo7分發。
對於須要向後兼容的代碼,能夠以註釋的形式添加類型註解。參見PEP 484的相關章節。
此文檔沒有版權限制。
來源: https://github.com/python/peps/blob/master/pep-0008.txt
Python之父 ↩
Barry的GNU Mailman編碼風格指南 http://barry.warsaw.us/software/STYLEGUIDE.txt ↩
出自Ralph Waldo Emerson。 ↩
掛起縮進是一種類型設置樣式,除了第一行外,段落中的全部行都縮進。在Python的上下文中,該術語用於描述括號括起來的語句的左括號是該行的最後一個非空格字符的樣式,後面的行將縮進直到右括號。 ↩
Donald Knuth的《The TeXBook》, 第195和196頁。 ↩
即用"""
而不是'''
↩
Typeshed repo https://github.com/python/typeshed ↩