Python PEP-8編碼風格指南中文版

#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

介紹(Introduction)

這篇文檔說明了Python主要發行版中標準庫代碼所遵照的規範。對於Python的C語言實現中的編碼規範,請參考實現Python的C代碼風格指南PEP 7git

這篇文檔和PEP 257(Docstring約定)都改編自Guido1最先的Python風格指南文章,而且添加了一些Barry的風格指南2中的內容。程序員

語言自身在發生着改變,隨着新的規範的出現和舊規範的過期,代碼風格也會隨着時間演變。github

不少項目都有本身的一套風格指南。若和本指南有任何衝突,應該優先考慮其項目相關的那套指南。web

保持盲目的一致是頭腦簡單的表現(A Foolish Consistency Is The Hobgoblin Of Little Minds)3

Guido的一個重要觀點是代碼被讀的次數遠多於被寫的次數。這篇指南旨在提升代碼的可讀性,使浩瀚如煙的Python代碼風格能保持一致。正如PEP 20那首《Zen of Python》的小詩裏所說的:「可讀性很重要(Readability counts)」。算法

這本風格指南是關於一致性的。同風格指南保持一致性是重要的,可是同項目保持一致性更加劇要,同一個模塊和一個函數保持一致性則最爲重要。express

然而最最重要的是:要知道什麼時候去違反一致性,由於有時風格指南並不適用。當存有疑慮時,請自行作出最佳判斷。請參考別的例子去作出最好的決定。而且不要猶豫,儘管提問。編程

特別的:千萬不要爲了遵照這篇PEP而破壞向後兼容性!api

若是有如下藉口,則能夠忽略這份風格指南:緩存

  1. 當採用風格指南時會讓代碼更難讀,甚至對於習慣閱讀遵循這篇PEP的代碼的人來講也是如此。

  2. 須要和周圍的代碼保持一致性,但這些代碼違反了指南中的風格(但是時歷史緣由形成的)——儘管這可能也是一個收拾別人爛攤子的機會(進入真正的極限編程狀態)。

  3. 如果有問題的某段代碼早於引入指南的時間,那麼沒有必要去修改這段代碼。

  4. 代碼須要和更舊版本的Python保持兼容,而舊版本的Python不支持風格指南所推薦的特性。

代碼佈局(Code Lay-Out)

縮進(Indentation)

每一個縮進級別採用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選項時,這些警告會變成錯誤。強烈推薦使用這些選項!

每行最大長度(Maximum Line Length)

將全部行都限制在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語句。

要確保續行的縮進適當。

二元運算符以前仍是以後換行?(Should a line break before or after a binary operator?)

長期以來一直推薦的風格是在二元運算符以後換行。可是這樣會影響代碼可讀性,包括兩個方面:一是運算符會分散在屏幕上的不一樣列上,二是每一個運算符會留在前一行並遠離操做數。因此,閱讀代碼的時候眼睛必須作更多的工做來肯定哪些操做數被加,哪些操做數被減:

# 錯誤的例子:運算符遠離操做數 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的風格。

空行(Blank Line)

使用2個空行來分隔最外層的函數(function)和類(class)定義。

使用1個空行來分隔類中的方法(method)定義。

可使用額外的空行(儘可能少)來分隔一組相關的函數。在一系列相關的僅佔一行的函數之間,空行也能夠被省略(好比一組虛函數定義)。

在函數內使用空行(儘可能少)使代碼邏輯更清晰。

Python支持control-L(如:^L)換頁符做爲空格;許多工具將這些符號做爲分頁符,所以你可使用這些符號來分頁或者區分文件中的相關區域。注意,一些編輯器和基於web的代碼預覽器可能不會將control-L識別爲分頁符,而是顯示成其餘符號。

源文件編碼(Source File Encoding)

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編碼。只有兩種例外:

  • (a) 測試狀況下爲了測試非ASCII編碼的特性
  • (b) 做者名字。做者名字不是由拉丁字母組成的也必須提供一個拉丁音譯名。

鼓勵具備全球受衆的開放源碼項目採用相似的原則。

模塊引用(Imports)

  • Imports應該分行寫,而不是都寫在一行,例如:

    # 分開寫 import os import sys # 不要像下面同樣寫在一行 import sys, os 

    這樣寫也是能夠的:

    from subprocess import Popen, PIPE 
  • Imports應該寫在代碼文件的開頭,位於模塊(module)註釋和文檔字符串(docstring)以後,模塊全局變量(globals)和常量(constants)聲明以前。

    Imports應該按照下面的順序分組來寫:

    1. 標準庫imports
    2. 相關第三方imports
    3. 本地應用/庫的特定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實現的接口,預先沒法知曉具體哪些定義將被覆蓋)。

    當使用這種方式從新發布命名時,指南後面關於公共和內部接口的部分仍然適用。

模塊級的雙下劃線命名(Module level dunder names)

模塊中的「雙下滑線」(變量名以兩個下劃線開頭,兩個下劃線結尾)變量,好比__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 

字符串引用(String Quotes)

在Python中表示字符串時,無論用單引號仍是雙引號都是同樣的。可是不推薦將這兩種方式看做同樣而且混用。最好選擇一種規則並堅持使用。當字符串中包含單引號時,採用雙引號來表示字符串,反之也是同樣,這樣能夠避免使用反斜槓,代碼也更易讀。

對於三引號表示的字符串,使用雙引號字符來表示6,這樣能夠和PEP 257的文檔字符串(docstring)規則保持一致。

表達式和語句中的空格(Whitespace In Expressions And Statements)

一些痛點(Pet Peeves)

在下列情形中避免使用過多的空白:

  • 方括號,圓括號和花括號以後:

    #正確的例子: 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 

其餘建議(Other Recommendations)

  • 避免任何行末的空格。由於它一般是不可見的,它可能會使人困惑:例如反斜槓後跟空格和換行符不會做爲續行標記。一些編輯器會自動去除行末空格,許多項目(如CPython自己)都有提交前的預處理鉤子來自動去除行末空格。
  • 在二元運算符的兩邊都使用一個空格:賦值運算符(=),增量賦值運算符(+=-= etc.),比較運算符(==<>!=<><=>=innot inisis not),布爾運算符(andornot)。
  • 若是使用了優先級不一樣的運算符,則在優先級較低的操做符周圍增長空白。請你自行判斷,不過永遠不要用超過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() 

什麼時候在末尾加逗號(When to use trailing commas)

末尾逗號一般是可選的,除非在定義單元素元組(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,) 

註釋(Comments)

和代碼矛盾的註釋還不如沒有。當代碼有改動時,必定要優先更改註釋使其保持最新。

註釋應該是完整的多個句子。若是註釋是一個短語或一個句子,其首字母應該大寫,除非開頭是一個以小寫字母開頭的標識符(永遠不要更改標識符的大小寫)。

若是註釋很短,結束的句號能夠被忽略。塊註釋一般由一段或幾段完整的句子組成,每一個句子都應該以句號結束。

你應該在句尾的句號後再加上2個空格。

使用英文寫做,參考Strunk和White的《The Elements of Style》

來自非英語國家的Python程序員們,請使用英文來寫註釋,除非你120%肯定你的代碼永遠不會被不懂你所用語言的人閱讀到。

塊註釋(Block Comments)

塊註釋通常寫在對應代碼以前,而且和對應代碼有一樣的縮進級別。塊註釋的每一行都應該以#和一個空格開頭(除非該文本是在註釋內縮進對齊的)。

塊註釋中的段落應該用只含有單個#的一行隔開。

行內註釋(Inline Comments)

儘可能少用行內註釋。

行內註釋是和代碼語句寫在一行內的註釋。行內註釋應該至少和代碼語句之間有兩個空格的間隔,而且以#和一個空格開始。

行內註釋一般不是必要的,在代碼含義很明顯時甚至會讓人分心。請不要這樣作:

x = x + 1 # x自加 

但這樣作是有用的:

x = x + 1 # 邊界補償 

文檔字符串(Documentation Strings)

要知道如何寫出好的文檔字符串(docstring),請參考PEP 257

  • 全部的公共模塊,函數,類和方法都應該有文檔字符串。對於非公共方法,文檔字符串不是必要的,但你應該留有註釋說明該方法的功能,該註釋應當出如今def的下一行。

  • PEP 257描述了好的文檔字符應該遵循的規則。其中最重要的是,多行文檔字符串以單行"""結尾,不能有其餘字符,例如:

    """Return a foobang
    
    Optional plotz says to frobnicate the bizbaz first.
    """
  • 對於僅有一行的文檔字符串,結尾處的"""應該也寫在這一行。

命名約定(Naming Conventions)

Python標準庫的命名約定有一些混亂,所以咱們永遠都沒法保持一致。但現在仍然存在一些推薦的命名標準。新的模塊和包(包括第三方框架)應該採用這些標準,但如果已經存在的包有另外一套風格的話,仍是應當與原有的風格保持內部一致。

首要原則(Overriding Principle)

對於用戶可見的公共部分API,其命名應當表達出功能用途而不是其具體的實現細節。

描述:命名風格(Descriptive: Naming Styles)

存在不少不一樣的命名風格,最好可以獨立地從命名對象的用途認出採用了哪一種命名風格。

一般區分如下命名樣式:

  • b (單個小寫字母)
  • B (單個大寫字母)
  • lowercase(小寫)
  • lower_case_with_underscores(帶下劃線小寫)
  • UPPERCASE(大寫)
  • UPPER_CASE_WITH_UNDERSCORES(帶下劃線大寫)
  • CapitalizedWords (也叫作CapWords或者CamelCase – 由於單詞首字母大寫看起來很像駝峯)。也被稱做StudlyCaps。

    注意:當CapWords裏包含縮寫時,將縮寫部分的字母都大寫。HTTPServerErrorHttpServerError要好。

  • 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__。請依照文檔描述來使用這些命名,千萬不要本身發明。

規範:命名約定(Prescriptive: Naming Conventions)

須要避免的命名(Names To Avoid)

不要使用字符’l’(L的小寫的字母),’O’(o大寫的字母),或者’I’(i的大寫的字母)來做爲單個字符的變量名。

在一些字體中,這些字符和數字1和0沒法區別開來。好比,當想使用’l’時,使用’L’代替。

ASCII兼容性(ASCII Compatibility)

標準庫中使用的標識符必須與ASCII兼容(參見PEP 3131中的policy這一節) 。

包和模塊命名(Package And Module Names)

模塊命名應短小,且爲全小寫。若下劃線能提升可讀性,也能夠在模塊名中使用。Python包命名也應該短小,且爲全小寫,但不該使用下劃線。

當使用C或C++寫的擴展模塊有相應的Python模塊提供更高級的接口時(好比,更加面向對象),C/C++模塊名如下劃線開頭(例如,_sociket)。

類命名(Class Names)

類命名應該使用駝峯(CapWords)的命名約定。

當接口已有文檔說明且主要是被用做調用時,也可使用函數的命名約定。

注意對於內建命名(builtin names)有一個特殊的約定:大部份內建名都是一個單詞(或者兩個一塊兒使用的單詞),駝峯(CapWords)的約定只對異常命名和內建常量使用。

類型變量命名(Type variable names)

PEP 484中引入的類型變量名稱一般應使用簡短的駝峯命名: TAnyStrNum。 建議將後綴_co_contra添加到用於聲明相應的協變(covariant)和逆變(contravariant)的行爲。例如:

from typing import TypeVar VT_co = TypeVar('VT_co', covariant=True) KT_contra = TypeVar('KT_contra', contravariant=True) 

異常命名(Exception Names)

因爲異常實際上也是類,所以類命名約定也適用與異常。不一樣的是,若是異常其實是拋出錯誤的話,異常名前應該加上」Error」的前綴。

全局變量命名(Global Variable Names)

(在此以前,咱們先假定這些變量都僅在同一個模塊內使用。)這些約定一樣也適用於函數命名。

對於引用方式設計爲from M import *的模塊,應該使用__all__機制來避免import全局變量,或者採用下劃線前綴的舊約定來命名全局變量,從而代表這些變量是「模塊非公開的」。

函數命名(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_OVERFLOWTOTAL 。

繼承的設計(Designing For Inheritance)

記得永遠區別類的方法和實例變量(屬性)應該是公開的仍是非公開的。若是有疑慮的話,請選擇非公開的;由於以後將非公開屬性變爲公開屬性要容易些。

公開屬性是那些你但願和你定義的類無關的客戶來使用的,而且確保不會出現向後不兼容的問題。非公開屬性是那些不但願被第三方使用的部分,你能夠不用保證非公開屬性不會變化或被移除。

咱們在這裏沒有使用「私有(private)」這個詞,由於在Python裏沒有什麼屬性是真正私有的(這樣設計省略了大量沒必要要的工做)。

另外一類屬性屬於子類API的一部分(在其餘語言中常常被稱爲」protected」)。一些類是爲繼承設計的,要麼擴展要麼修改類的部分行爲。當設計這樣的類時,須要謹慎明確地決定哪些屬性是公開的,哪些屬於子類API,哪些真的只會被你的基類調用。

請記住以上幾點,下面是Python風格的指南:

  • 公開屬性不該該有開頭下劃線。

  • 若是公開屬性的名字和保留關鍵字有衝突,在你的屬性名尾部加上一個下劃線。這比採用縮寫和簡寫更好。(然而,和這條規則衝突的是,‘cls’對任何變量和參數來講都是一個更好地拼寫,由於你們都知道這表示class,特別是在類方法的第一個參數裏。)

    注意 1:對於類方法,參考以前的參數命名建議。

  • 對於簡單的公共數據屬性,最後僅公開屬性名字,不要公開復雜的調用或設值方法。請記住,若是你發現一個簡單的數據屬性須要增長功能行爲時,Python爲功能加強提供了一個簡單的途徑。這種狀況下,使用Properties註解將功能實現隱藏在簡單數據屬性訪問語法以後。

    注意 1:Properties註解僅僅對新風格類有用。

    注意 2:儘可能保證功能行爲沒有反作用,儘管緩存這種反作用看上去並無什麼大問題。

    注意 3: 對計算量大的運算避免試用properties;屬性的註解會讓調用者相信訪問的運算量是相對較小的。

  • 若是你的類將被子類繼承的話,你有一些屬性並不想讓子類訪問,考慮將他們命名爲兩個下劃線開頭而且結尾處沒有下劃線。這樣會觸發Python命名修飾算法,類名會被修飾添加到屬性名中。這樣能夠避免屬性命名衝突,以避免子類會不經意間包含相同的命名。

    注意 1:注意命名修飾僅僅是簡單地將類名加入到修飾名中,因此若是子類有相同的類名合屬性名,你可能仍然會遇到命名衝突問題。

    注意 2:命名修飾能夠有特定用途,好比在調試時,__getattr__()比較不方便。然而命名修飾算法的能夠很好地記錄,而且容意手動執行。

    注意 3:不是全部人都喜歡命名修飾。須要試着去平衡避免偶然命名衝突的需求和高級調用者使用的潛在可能性。

公開和內部接口(Public And Internal Interfaces)

任何向後兼容性保證僅對公開接口適用。相應地,用戶可以清楚分辨公開接口和內部接口是很重要的。

文檔化的接口被認爲是公開的,除非文檔中明確申明瞭它們是臨時的或者內部接口,不保證向後兼容性。全部文檔中未提到的接口應該被認爲是內部的。

爲了更好審視公開接口和內部接口,模塊應該在__all屬性中明確申明公開API是哪些。將__all__設爲空list表示該模塊中沒有公開API。

即便正確設置了__all屬性,內部接口(包,模塊,類,函數,屬性或其餘命名)也應該以一個下劃線開頭。

若是接口的任一一個命名空間(包,模塊或類)是內部的,那麼該接口也應該是內部的。

引用的命名應該永遠被認爲是實現細節。其餘模塊不該當依賴這些非直接訪問的引用命名,除非它們在文檔中明確地被寫爲模塊的API,例如os.path或者包的__init__模塊,那些從子模塊展示的功能。

編程建議(Programming Recommendations)

  • 代碼應該以不影響其餘Python實現(PyPy,Jython,IronPython,Cython,Psyco等)的方式編寫。

    例如,不要依賴於 CPython 在字符串拼接時的優化實現,像這種語句形式a += ba = a + b。即便是 CPython(僅對某些類型起做用) 這種優化也是脆弱的,不是在全部的實現中都不使用引用計數。在庫中性能敏感的部分,用''.join形式來代替。這會確保在全部不一樣的實現中字符串拼接是線性時間的。

  • 與單例做比較,像None應該用isis 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 == yx != 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:語句將會捕獲到SystemExitKeyboardInterrupt異常,很難區分程序的中斷究竟是Ctrl+C仍是其餘問題引發的。若是你想捕獲程序的全部錯誤,使用except Exception:(空except:等同於except BaseException)。

    一個好的經驗是限制使用空except語句,除了這兩種狀況:

    1. 若是異常處理程序會打印出或者記錄回溯信息;至少用戶意識到錯誤的存在。
    2. 若是代碼須要作一些清理工做,但後面用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 裏面,strunicode有一個公共的基類叫basestring,所以你能夠這樣作:

    if isinstance(obj, basestring): 

    注意,在 Python 3 裏面,unicodebasestring已經不存在了(只有str),byte對象再也不是字符串的一種(被一個整數序列替代)。

  • 對於序列(字符串、列表、元組)來講,空的序列爲False

    正確的寫法:

    if not seq: if seq: 

    錯誤的寫法:

    if len(seq): if not len(seq): 
  • 不要讓字符串對尾隨的空格有依賴。這樣的尾隨空格是視覺上沒法區分的,一些編輯器(或者,reindent.py)會將其裁剪掉。

  • 不要用==比較TrueFalse

    #推薦的寫法: if greeting: #不推薦的寫法: if greeting == True: #更加不推薦的寫法: if greeting is True: 

函數註解(Function Annotations)

隨着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的相關章節

版權(Copyright)

此文檔沒有版權限制。

來源: https://github.com/python/peps/blob/master/pep-0008.txt

  1. Python之父 

  2. Barry的GNU Mailman編碼風格指南 http://barry.warsaw.us/software/STYLEGUIDE.txt 

  3. 出自Ralph Waldo Emerson。 

  4. 掛起縮進是一種類型設置樣式,除了第一行外,段落中的全部行都縮進。在Python的上下文中,該術語用於描述括號括起來的語句的左括號是該行的最後一個非空格字符的樣式,後面的行將縮進直到右括號。 

  5. Donald Knuth的《The TeXBook》, 第195和196頁。 

  6. 即用"""而不是''' 

  7. Typeshed repo https://github.com/python/typeshed 

相關文章
相關標籤/搜索