python代碼風格指南:pep8 中文翻譯

摘要python

本文給出主Python版本標準庫的編碼約定。CPythonC代碼風格參見​PEP7本文和​PEP 257 文檔字符串標準改編自Guido最初的《Python Style Guide, 並增長了Barry​GNU Mailman Coding Style Guide的部份內容。本文會隨着語言改變等而改變。許多項目都有本身的編碼風格指南,衝突時本身的指南爲準。git

本文給出主Python版本標準庫的編碼約定。CPythonC代碼風格參見PEP7程序員

本文和PEP 257 文檔字符串標準改編自Guido最初的《Python Style Guide, 並增長了BarryGNU Mailman Coding Style Guide的部份內容。編程

本文會隨着語言改變等而改變。api

許多項目都有本身的編碼風格指南,衝突時本身的指南爲準。數組

一致性考慮框架

Guido的關鍵點之一是:代碼更可能是用來讀而不是寫。本指南旨在改善Python代碼的可讀性,即PEP 20所說的"可讀性計數"(Readability counts)socket

風格指南強調一致性。項目、模塊或函數保持一致都很重要。編輯器

最重要的是知道什麼時候不一致, 有時風格指南並不適用。當有疑惑時運用你的最佳判斷,參考其餘例子並多問!ide

特別注意:不要由於遵照本PEP而破壞向後兼容性!

部分能夠違背指南狀況:

  • 遵循指南會下降可讀性。
  • 與周圍其餘代碼不一致。
  • 代碼在引入指南完成,暫時沒有理由修改。
  • 舊版本兼容。

代碼佈局

縮進

每級縮進用4個空格。

括號中使用垂直隱式縮進或使用懸掛縮進。後者應該注意第一行要沒有參數,後續行要有縮進。

  • Yes

# 對準左括號

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)

  • No

# 不使用垂直對齊時,第一行不能有參數。

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語句跨行時,兩個字符關鍵字(好比if)加上一個空格,再加上左括號構成了很好的縮進。後續行暫時沒有規定,至少有以下三種格式,建議使用第3種。

# 沒有額外縮進,不是很好看,我的不推薦.

if (this_is_one_thing and

    that_is_another_thing):

    do_something()

 

# 添加註釋

if (this_is_one_thing and

    that_is_another_thing):

    # Since both conditions are true, we can frobnicate.

    do_something()

 

# 額外添加縮進,推薦。

# Add some extra indentation on the conditional continuation line.

if (this_is_one_thing

        and that_is_another_thing):

    do_something()

右邊括號也能夠另起一行。有兩種格式,建議第2種。

# 右括號不回退,我的不推薦

my_list = [

    123,

    456,

    ]

result = some_function_that_takes_arguments(

    'a', 'b', 'c',

    'd', 'e', 'f',

    )

 

# 右括號回退

my_list = [

    123,

    456,

]

result = some_function_that_takes_arguments(

    'a', 'b', 'c',

    'd', 'e', 'f',

)

空格或Tab?

  • 空格是首選的縮進方法。
  • Tab僅僅在已經使用tab縮進的代碼中爲了保持一致性而使用。
  • Python 3中不容許混合使用Tab和空格縮進。
  • Python 2的包含空格與Tab和空格縮進的應該所有轉爲空格縮進。

Python2命令行解釋器使用-t選項時有非法混合Tab和空格的狀況會告警。當使用-tt警告提高爲錯誤。強烈推薦這些選項!另外我的推薦pep8autopep8模塊。

最大行寬

限制全部行的最大行寬爲79字符。

文本長塊,好比文檔字符串或註釋,行長度應限制爲72個字符。

多數工具默認的續行功能會破壞代碼結構,使它更難理解,不推薦使用。可是超過80個字符加以提醒是必要的。一些工具可能根本不具有動態換行功能。

一些團隊強烈但願更長的行寬。若是能達成一致,能夠從從80提升到100個字符(最多99個字符)增長了標稱線的長度,不過依舊建議文檔字符串和註釋保持在72的長度。

Python標準庫比較保守,限制行寬79個字符(文檔字符串/註釋72)。

續行的首選方法是使用小括號、中括號和大括號反斜線仍可能在適當的時候。其次是反斜槓。好比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())

相似的還有assert

注意續行要儘可能不影響可讀性。好比一般在二元運算符以後續行:

class Rectangle(Blob):

 

    def __init__(self, width, height,

                 color='black', emphasis=None, highlight=0):

        if (width == 0 and height == 0 and

                color == 'red' and emphasis == 'strong' or

                highlight > 100):

            raise ValueError("sorry, you lose")

        if width == 0 and height == 0 and (color == 'red' or

                                           emphasis is None):

            raise ValueError("I don't think so -- values are %s, %s" %

                             (width, height))

        Blob.__init__(self, width, height,

                      color, emphasis, highlight)

空行

  • 兩行空行分割頂層函數和類的定義。
  • 類的方法定義用單個空行分割。
  • 額外的空行能夠必要的時候用於分割不一樣的函數組,可是要儘可能節約使用。
  • 額外的空行能夠必要的時候在函數中用於分割不一樣的邏輯塊,可是要儘可能節約使用。
  • Python contol-L做爲空白符;許多工具視它爲分頁符,這些要因編輯器而異。

源文件編碼

在覈心Python發佈的代碼應該老是使用UTF-8(ASCIIPython 2)

ASCII文件(Python 2)UTF-8(Python 3)不該有編碼聲明。

標準庫中非默認的編碼應僅用於測試或當註釋或文檔字符串,好比包含非ASCII字符的做者姓名,儘可能使用\x , \u , \U , or \N

Python 3.0及之後版本,PEP 3131可供參考,部份內容以下:在Python標準庫必須使用ASCII標識符,並儘可能只使用英文字母。此外字符串和註釋也必須用ASCII。惟一的例外是:(a)測試非ASCII的功能,和(b)做者的名字不是拉丁字母。

導入

  • 導入在單獨行

Yes

import os

import sys

from subprocess import Popen, PIPE

No:

import sys, os

  • 導入始終在文件的頂部,在模塊註釋和文檔字符串以後,在模塊全局變量和常量以前。

導入順序以下:標準庫進口,相關的第三方庫,本地庫。各組的導入之間要有空行。

相關的all放在導入以後。

  • 推薦絕對路徑導入,由於它們一般更可讀,並且每每是表現更好的(或至少提供更好的錯誤消息。

import mypkg.sibling

from mypkg import sibling

from mypkg.sibling import example

在絕對路徑比較長的狀況下,也可使用相對路徑:

from . import sibling

from .sibling import example

Python 3中已經禁止隱式的相對導入。

  • 導入類的方法:

from myclass import MyClass

from foo.bar.yourclass import YourClass

若是和本地名字有衝突:

import myclass

import foo.bar.yourclass

  • 禁止使用通配符導入。

通配符導入(from <module> import *)應該避免,由於它不清楚命名空間有哪些名稱存,混淆讀者和許多自動化的工具。惟一的例外是從新發布對外的API時能夠考慮使用。

字符串引用

Python中單引號字符串和雙引號字符串都是相同的。注意儘可能避免在字符串中的反斜槓以提升可讀性。

根據PEP 257, 三個引號都使用雙引號。

表達式和語句中的空格

強制要求

  • 括號裏邊避免空格

# 括號裏邊避免空格

Yes

spam(ham[1], {eggs: 2})

No

spamham[ 1 ], { eggs: 2 } )

  • 逗號,冒號,分號以前避免空格

# 逗號,冒號,分號以前避免空格

# Yes

if x =4: print x, y; x, y = y, x

# No

if x =4 : print x , y ; x , y = y , x

  • 索引操做中的冒號看成操做符處理先後要有一樣的空格(一個空格或者沒有空格,我的建議是沒有。

# Yes

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]

# No

ham[lower + offset:upper + offset]

ham[19], ham[1 :9], ham[1:9 :3]

ham[lower : : upper]

ham[ : upper]

函數調用的左括號以前不能有空格

# Yes

spam(1)

dct['key'] = lst[index]

 

# No

spam (1)

dct ['key'] = lst [index]

  • 賦值等操做符先後不能由於對齊而添加多個空格

# Yes

x = 1

y = 2

long_variable = 3

 

# No

x             = 1

y             = 2

long_variable = 3

其餘建議

  • 二元運算符兩邊放置一個空格:

涉及 =、符合操做符 ( += , -=)、比較( == , < , > , != , <> , <= , >= , in , not in , is , is not )、布爾( and , or , not )

  • 優先級高的運算符或操做符的先後不建議有空格。

# Yes

i = i + 1

submitted += 1

x = x*2 - 1

hypot2 = x*x + y*y

c = (a+b) * (a-b)

 

# No

i=i+1

submitted +=1

x = x * 2 - 1

hypot2 = x * x + y * y

c = (a + b) * (a - b)

  • 關鍵字參數和默認值參數的先後不要加空格

# Yes

def complex(real, imag=0.0):

    return magic(r=real, i=imag)

 

# No

def complex(real, imag = 0.0):

    return magic(r = real, i = imag)

  • 函數註釋中,=先後要有空格,冒號和"->"的前面無空格,後面有空格。

# Yes

def munge(input: AnyStr):

def munge(sep: AnyStr = None):

def munge() -> AnyStr:

def munge(input: AnyStr, sep: AnyStr = None, limit=1000):

 

# No

def munge(input: AnyStr=None):

def munge(input:AnyStr):

def munge(input: AnyStr)->PosInt:

  • 一般不推薦複合語句(Compound statements: 多條語句寫在同一行)

# Yes

if foo == 'blah':

    do_blah_thing()

do_one()

do_two()

do_three()

 

# No

if foo == 'blah'do_blah_thing()

do_one(); do_two(); do_three()

  • 儘管有時能夠在if/for/while 的同一行跟一小段代碼,但毫不要跟多個子句,並儘可能避免換行。

# No

if foo == 'blah'do_blah_thing()

for x in lst: total += x

while t < 10: t = delay()

更不是:

# No

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()

註釋

與代碼自相矛盾的註釋比沒註釋更差。修改代碼時要優先更新註釋!

註釋是完整的句子。若是註釋是斷句,首字母應該大寫,除非它是小寫字母開頭的標識符(永遠不要修改標識符的大小寫)

若是註釋很短,能夠省略末尾的句號。註釋塊一般由一個或多個段落組成。段落由完整的句子構成且每一個句子應該以點號(後面要有兩個空格)結束,並注意斷詞和空格。

非英語國家的程序員請用英語書寫你的註釋,除非你120%確信代碼永遠不會被不懂你的語言的人閱讀。

註釋塊

註釋塊一般應用在代碼前,並和這些代碼有一樣的縮進。每行以 '# '(除非它是註釋內的縮進文本,注意#後面有空格)

註釋塊內的段落用僅包含單個 '#' 的行分割。

行內註釋

慎用行內註釋(Inline Comments) 節儉使用行內註釋。行內註釋是和語句在同一行,至少用兩個空格和語句分開。行內註釋不是必需的,重複羅嗦會令人分心。不要這樣作:

x = x + 1 # Increment x

但有時頗有必要:

x = x + 1 # Compensate for border

文檔字符串

文檔字符串的標準參見:PEP 257

  • 爲全部公共模塊、函數、類和方法書寫文檔字符串。非公開方法不必定有文檔字符串,建議有註釋(出如今 def 行以後)來描述這個方法作什麼。
  • 更多參考:PEP 257 文檔字符串約定。注意結尾的 """ 應該單獨成行,例如:
  • """Return a foobang
  • Optional plotz says to frobnicate the bizbaz first.

    """

  • 單行的文檔字符串,結尾的 """ 在同一行。

版本標籤

版本註記 (Version Bookkeeping)

若是你必須在源文件中包含gitSubversionCVSRCS crud信息,放置在模塊的文檔字符串以後,任何其餘代碼以前,上下各用一個空行:

__version__ = "$Revision$"# $Source$

命名約定

Python庫的命名約定有點混亂,不可能徹底一致。但依然有些廣泛推薦的命名規範的。新的模塊和包 (包括第三方的框架) 應該遵循這些標準。對不一樣風格的已有的庫,建議保持內部的一致性。

最重要的原則

用戶可見的API命名應遵循使用約定而不是實現。

描述:命名風格

有多種命名風格:

  • b(單個小寫字母)
  • B(單個大寫字母)
  • lowercase(小寫串)
  • lower_case_with_underscores(帶下劃線的小寫)
  • UPPERCASE(大寫串)
  • UPPER_CASE_WITH_UNDERSCORES(帶下劃線的大寫串)
  • CapitalizedWords(首字母大寫的單詞串或駝峯縮寫)

注意: 使用大寫縮寫時,縮寫使用大寫字母更好。故 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__(雙先後下劃線):用戶名字空間的魔法對象或屬性。例如:__init__ , __import__ or __file__,不要本身發明這樣的名字。

命名約定規範

  • 避免採用的名字

決不要用字符'l'(小寫字母el)'O'(大寫字母oh),或 'I'(大寫字母eye) 做爲單個字符的變量名。一些字體中,這些字符不能與數字10區別。用'L' 代替'l'時。

  • 包和模塊名

模塊名要簡短,所有用小寫字母,可以使用下劃線以提升可讀性。包名和模塊名相似,但不推薦使用下劃線。

模塊名對應到文件名,有些文件系統不區分大小寫且截短長名字,在 Unix上不是問題,但當把代碼遷移到 MacWindows DOS 上時,就多是個問題。固然隨着系統的演進,這個問題已經不是常常出現。

另外有些模塊底層用CC++ 書寫,並有對應的高層Python模塊,C/C++模塊名有一個前置下劃線 (如:_socket)

  • 類名

遵循CapWord

接口須要文檔化而且能夠調用時,可能使用函數的命名規則。

注意大部份內置的名字是單個單詞(或兩個),CapWord只適用於異常名稱和內置的常量。

  • 異常名

若是確實是錯誤,須要在類名添加後綴 "Error"

  • 全局變量名

變量儘可能只用於模塊內部,約定相似函數。

對設計爲經過 "from M import " 來使用的模塊,應採用 __all__ 機制來防止導入全局變量;或者爲全局變量加一個前置下劃線。

  • 函數名

函數名應該爲小寫,必要時可用下劃線分隔單詞以增長可讀性。 mixedCase(混合大小寫)僅被容許用於兼容性考慮(: threading.py)

  • 函數和方法的參數

實例方法第一個參數是 'self'

類方法第一個參數是 'cls'

若是函數的參數名與保留關鍵字衝突,一般在參數名後加一個下劃線。

  • 方法名和實例變量

同函數命名規則。

非公開方法和實例變量增長一個前置下劃線。

爲避免與子類命名衝突,採用兩個前置下劃線來觸發重整。類Foo屬性名爲__a不能以 Foo.__a訪問。(執著的用戶仍是能夠經過Foo._Foo__a) 一般雙前置下劃線僅被用來避免與基類的屬性發生命名衝突。

  • 常量

常量一般在模塊級定義,由大寫字母用下劃線分隔組成。好比括MAX_OVERFLOWTOTAL

  • 繼承設計

考慮類的方法和實例變量(統稱爲屬性)是否公開。若是有疑問,選擇不公開;把其改成公開比把公開屬性改成非公開要容易。

公開屬性可供全部人使用,並一般向後兼容。非公開屬性不給第三方使用、可變甚至被移除。

這裏不使用術語"private" Python中沒有屬性是真正私有的。

另外一類屬性是子類API(在其餘語言中一般稱爲 "protected")一些類被設計爲基類,能夠擴展和修改。

謹記這些Python指南:

  1. 公開屬性應該沒有前導下劃線。
  2. 若是公開屬性名和保留關鍵字衝突,能夠添加後置下劃線
  3. 簡單的公開數據屬性,最好只公開屬性名,沒有複雜的訪問/修改方法,pythonProperty提供了很好的封裝方法。 d.若是不但願子類使用的屬性,考慮用兩個前置下劃線(沒有後置下劃線)命名。

公共和內部接口

任何向後兼容的保證只適用於公共接口。

文檔化的接口一般是公共的,除非明說明是臨時的或爲內部接口、其餘全部接口默認是內部的。

爲了更好地支持內省,模塊要在__all__屬性列出公共API

內部接口要有前置下劃線。

若是命名空間(包、模塊或類)是內部的,裏面的接口也是內部的。

導入名稱應視爲實現細節。其餘模塊不能間接訪名字,除非在模塊的API文檔中明確記載,如os.path中或包的__init__暴露了子模塊。

編程建議

  • 考慮多種Python實現(PyPy, Jython, IronPython,Pyrex, Psyco, 等等)

例如,CPythona+=ba=a+b等語句有高效的實現,但在Jython中運行很慢,儘可能改用.join()

  • None比較用'is''is not',不要用等號。

注意"if x is not None" "if x" 的區別。

  • "is not"代替"not ... is"。前者的可讀性更好。

# Yes

if foo is not None

 

# No

if not foo is None

  • 使用基於類的異常。

比較排序操做最好是實現全部六個操做,而不是代碼中實現比較邏輯。functools.total_ordering()裝飾符能夠生成缺失的比較方法。

__eq__,__ne__,__lt__,__lt__,__gt__,____)

PEP207 比較標準代表反射規則由Python完成。所以解釋器可能會交換參數的位置,好比替換y > xx < y,因此有必要實現這5種方法。

  • 使用函數定義def代替lambda賦值給標識符:

# Yes

def f(x): 

    return 2*x

 

# No

f = lambda x: 2*x

前者更適合回調和字符串表示。

  • 異常類繼承自Exception,而不是BaseException

源於異常,而不是BaseException例外。從BaseException直接繼承的例外狀況追趕他們幾乎老是錯誤的事情作保留。

要設計基於層次的異常,捕捉到須要的異常,而不是異常引起的位置。能回答:"出了什麼問題?",而不是僅僅指出"問題發生"(更多參考:PEP3151 重構OSIO異常層次

  • 適當使用異常鏈。在Python3"raise X from Y"明確表示更換且保留了原來的traceback

替換內部異常(Python2: "raise X""raise X from None")時,確保相關細節轉移到新的異常(如轉換KeyErrorAttributeError保存屬性名,或在新的異常中嵌入原始異常)

  • Python2中用" raise ValueError('message')"代替"raise ValueError, 'message'"

後者不兼容Python3語法。前者續行方便。

  • 捕獲異常時儘可能指明具體異常,而不是空"except:"子句。好比:

# Yes

try:

    import platform_specific_module

except ImportError:

    platform_specific_module = None

"except:"子句(至關於except Exception)會捕捉SystemExitKeyboardInterrupt異常,難以用Control-C中斷程序,並可掩蓋其餘問題。若是你捕捉信號錯誤以外全部的異常,使用"except Exception"

"except:"子句適用的狀況兩種狀況:

a, 打印出或記錄了traceback,至少讓用戶將知道已發生錯誤。 b, 代碼須要作一些清理工做,並用 raise轉發了異常。這樣try...finally能夠捕捉到它。

  • Python 2.6之後建議用as顯示綁定異常名:

# Yes

try:

    process_data()

except Exception as exc:

    raise DataProcessingFailedError(str(exc))

這樣才能兼容Python3語法並避免歧義。

  • 捕捉操做系統錯誤時,建議使用Python 3.3引入顯式異常層次,支持內省errno值。
  • 此外全部try/except子句的代碼要儘可的少,以避免屏蔽其餘的錯誤。

# Yes

try:

    value = collection[key]

except KeyError:

    return key_not_found(key)

else:

    return handle_value(value)

 

# No

try:

    # 太泛了!

    return handle_value(collection[key])

except KeyError:

    # 會捕捉到handle_value()中的KeyError

    return key_not_found(key)

  • 本地資源建議使用with語句,以確保即時清理。固然try / finally語句也是能夠接受的。
  • 上下文管理器在作獲取和釋放資源以外的事情時,應經過獨立的函數或方法。例如:

# Yes

with conn.begin_transaction():

    do_stuff_in_transaction(conn)

 

# No

with conn:

    do_stuff_in_transaction(conn)

後者指明enterexit方法。

  • 函數或者方法在沒有返回時要明確返回None

# Yesdef foo(x):

    if x >= 0:

        return math.sqrt(x)

    else:

        return Nonedef bar(x):

    if x < 0:

        return None

    return math.sqrt(x)# Nodef foo(x):

    if x >= 0:

        return math.sqrt(x)def bar(x):

    if x < 0:

        return

    return math.sqrt(x)

  • 使用字符串方法而不是string模塊。

python 2.0之後字符串方法老是更快,且Unicode字符串相同的API

  • 使用使用 .startswith().endswith()代替字符串切片來檢查前綴和後綴。and

startswith()endswith更簡潔,利於減小錯誤。例如:

# Yes

if foo.startswith('bar'):

 

# No

if foo[:3] == 'bar':

  • 使用isinstance()代替對象類型的比較:

# Yes

if isinstance(obj, int):

 

# No

if type(obj) is type(1):

檢查是不是字符串時,注意Python 2strunicode有公共的基類:

if isinstance(obj, basestring): Python 2.2 中,types 模塊爲此定義了 StringTypes 類型,例如:

# Yes

if isinstance(obj, basestring):

Python3Unicodebasestring的再也不存在(只有str)和字節對象再也不是字符串(是整數序列)

  • 對序列(字符串、列表、元組), 空序列爲false:

# Yes

if not seq:

   pass

if seq:

   pass

 

# No

if len(seq):

   pass

if not len(seq):

   pass

  • 字符串後面不要有大量拖尾空格。
  • 不要用 == 進行布爾比較

# Yes

if greeting::

   pass

 

# No

if greeting == True

   pass

if greeting is True# Worse

   pass

  • Python標準庫不使用的功能註釋,這塊有待用戶去發現和體驗有用的註釋風格。下面有些第三方的建議()
相關文章
相關標籤/搜索