Python編碼規(guī)范

介紹

本文檔給出了Python代碼的編碼約定溶褪。

該樣式指南會隨著時間的流逝而發(fā)展入问,因為會發(fā)現(xiàn)其他約定,而過去的約定由于語言本身的更改而變得過時芽突。

許多項目都有自己的編碼風格準則试浙。如有任何沖突,此類項目特定的指南優(yōu)先于該項目寞蚌。

一致性

代碼的讀取次數(shù)比編寫的次數(shù)多田巴。此處提供的指南旨在提高代碼的可讀性,并使其在各種Python代碼中保持一致挟秤。正如PEP 20所說壹哺,“可讀性至關重要”。

代碼布局

縮進

每個縮進級別使用4個空格艘刚。

續(xù)行可以和括號相鄰元素對齊管宵,或使用懸掛式縮進

Yes:

# Aligned with opening delimiter.
foo = long_function_name(var_one, var_two,
                         var_three, var_four)

# More indentation included to distinguish this from the rest.
def long_function_name(
        var_one, var_two, var_three,
        var_four):
    print(var_one)

# Hanging indents should add a level.
foo = long_function_name(
    var_one, var_two,
    var_three, var_four)

No:

# Arguments on first line forbidden when not using vertical alignment.
foo = long_function_name(var_one, var_two,
    var_three, var_four)

# Further indentation required as indentation is not distinguishable.
def long_function_name(
    var_one, var_two, var_three,
    var_four):
    print(var_one)

對于連續(xù)行,4空格規(guī)則是可選的。

可選的:

# Hanging indents *may* be indented to other than 4 spaces.
foo = long_function_name(
  var_one, var_two,
  var_three, var_four)

if語句的條件部分足夠長而要求將其寫成多行時箩朴,可接受的選項包括但不限于:

# No extra indentation.
if (this_is_one_thing and
    that_is_another_thing):
    do_something()

# Add a comment, which will provide some distinction in editors
# supporting syntax highlighting.
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()

(另請參見下面有關在二進制運算符之前還是之后中斷的討論岗喉。)

多行構造的右花括號/括號/括號可以在列表最后一行的第一個非空白字符下對齊,如下所示:

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',
)

制表符或空格钱床?

空格是首選的縮進方法。

不推薦使用制表符

Python 3不允許混合使用制表符和空格進行縮進埠居。

縮進制表符和空格混合在一起的Python 2代碼應轉換為使用空格查牌。

最大長度

限制所有行最多79個字符。

通過限制所需的編輯器窗口寬度滥壕,可以并排打開多個文件纸颜,并且在使用在相鄰列中顯示兩個版本的代碼查看工具時,效果很好绎橘。

大多數(shù)工具中的默認包裝會破壞代碼的視覺結構胁孙,使其更難以理解。選擇這些限制是為了避免在窗口寬度設置為80的編輯器中進行換行称鳞,即使在換行時該工具在最后一列中放置了標志符號也是如此浊洞。某些基于Web的工具可能根本不提供動態(tài)換行。

一些團隊喜歡更長的線長胡岔。對于專門或主要由可以在此問題上達成協(xié)議的團隊維護的代碼,可以將標稱行長度從80個字符增加到100個字符(有效地將最大長度增加到99個字符)枷餐。

換行的首選方法是在括號靶瘸,方括號和花括號內使用Python的隱含行連續(xù)性。通過將表達式包裝在括號中毛肋,可以將長行分成多行怨咪。應優(yōu)先使用這些,而不是使用反斜杠進行行連續(xù)润匙。

有時反斜杠仍然適用诗眨。例如,長的多with語句不能使用隱式連續(xù)孕讳,因此可以使用反斜杠:

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

確保適當縮進續(xù)行匠楚。

換行符應該在二進制運算符之前還是之后?

幾十年來厂财,推薦的樣式是二元運算符之后換行芋簿。但這會以兩種方式損害可讀性:運算符趨向于分散在屏幕上的不同列上,并且每個運算符都從其操作數(shù)移至上一行璃饱。在這里与斤,眼睛必須做額外的工作才能分辨出添加了哪些項目和減去了哪些項目:

# No: operators sit far away from their operands
income = (gross_wages +
          taxable_interest +
          (dividends - qualified_dividends) -
          ira_deduction -
          student_loan_interest)

為了解決此可讀性問題,數(shù)學家及其發(fā)布者遵循相反的約定。Donald Knuth在他的“計算機和排版”系列中解釋了傳統(tǒng)規(guī)則:

“盡管段落中的公式總是在二進制運算和關系之后中斷撩穿,但顯示的公式總是在二進制運算和關系之前中斷”

遵循數(shù)學的傳統(tǒng)磷支,通常會導致代碼更具可讀性:

# Yes: easy to match operators with operands
income = (gross_wages
          + taxable_interest
          + (dividends - qualified_dividends)
          - ira_deduction
          - student_loan_interest)

在Python代碼中,只要約定在本地是一致的食寡,就可以在二進制運算符之前或之后中斷雾狈。對于新代碼,建議使用Knuth的樣式冻河。

空行

用兩個空行分隔頂級函數(shù)和類定義箍邮。

類內的方法定義由單個空白行分隔。

多余的空白行可以(分別)用于分隔相關功能組叨叙。

在函數(shù)中使用空白行锭弊,以區(qū)分各個邏輯部分。

源文件編碼

核心Python發(fā)行版中的代碼應始終使用UTF-8(或Python 2中的ASCII)擂错。

使用ASCII(在Python 2中)或UTF-8(在Python 3中)的文件不應具有編碼聲明味滞。

在標準庫中,非默認編碼僅應用于測試目的钮呀,或者在注釋或文檔字符串需要提及包含非ASCII字符的作者姓名時剑鞍;否則,使用\x爽醋,\u蚁署,\U,或\N逃逸是包含在字符串非ASCII數(shù)據(jù)的首選方式蚂四。

對于Python 3.0及更高版本光戈,標準庫規(guī)定了以下策略(請參閱PEP 3131):Python標準庫中的所有標識符務必使用純ASCII標識符,并且在可行的情況下應使用英文單詞(在許多情況下遂赠,縮寫和技術使用的術語不是英語)久妆。此外,字符串文字和注釋也必須使用ASCII跷睦。唯一的例外是(a)測試非ASCII功能的測試用例筷弦,以及(b)作者的姓名。名稱不基于拉丁字母的作者必須提供其姓名的拉丁音譯抑诸。

鼓勵具有全球受眾的開源項目采取類似的政策烂琴。

Import

  • 導入通常應放在單獨的行上,例如:

    Yes:

    import os
    import sys
    

    No:

    import os, sys
    

    可以這樣寫:

    from subprocess import Popen, PIPE
    
  • 導入總是放在文件的頂部哼鬓,緊隨任何模塊注釋和文檔字符串之后监右,以及模塊全局變量和常量之前。

    導入應按以下順序分組:

    1. 標準庫導入
    2. 相關第三方進口
    3. 本地應用程序/特定于庫的導入

    您應該在每組導入之間放置一個空白行异希。

  • 推薦使用絕對導入健盒,因為如果導入系統(tǒng)配置不正確(例如绒瘦,程序包中的目錄最終位于時sys.path),則它們通常更易于閱讀扣癣,并且通常表現(xiàn)得更好(或至少會提供更好的錯誤消息):

    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
    

    并使用myclass.MyClassfoo.bar.yourclass.YourClass

  • from <module> import *應避免使用通配符導入笨蚁,因為通配符不能弄清楚名稱空間中存在哪些名稱睹晒,這會混淆閱讀器和許多自動化工具。通配符導入有一個合理的用例括细,它是將內部接口重新發(fā)布為公共API的一部分

    以這種方式重新發(fā)布名稱時伪很,以下有關公共和內部接口的準則仍然適用。

模塊級dunder名稱

模塊級“dunders”(即名稱具有兩個前下劃線和兩個后下劃線)奋单,例如__all__锉试,__author____version__等應被放置在模塊文檔字符串之后览濒,但在任何導入語句除了 from __future__ import之后键痛。Python要求future import必須在模塊中出現(xiàn)在除文檔字符串以外的任何其他代碼之前。

例如:

"""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中匾七,單引號字符串和雙引號字符串是相同的。本PEP對此不做任何建議江兢。選擇一條規(guī)則并堅持下去昨忆。但是,當字符串包含單引號或雙引號字符時杉允,請使用另一個以避免在字符串中使用反斜杠邑贴。它提高了可讀性。

對于三引號字符串叔磷,請始終使用雙引號字符以與PEP 257中的docstring約定一致拢驾。

表達式和語句中的空格

一些習慣

在以下情況下,請避免使用多余的空格:

  • 緊靠在括號改基,方括號或大括號內:

    Yes:

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

    No:

    spam( ham[ 1 ], { eggs: 2 } )
    
  • 在尾隨逗號和后面的右括號之間:

    Yes:

    foo = (0,)
    

    No:

    bar = (0, )
    
  • 在逗號繁疤,分號或冒號之前:

    Yes:

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

    No:

    if x == 4 : print x , y ; x , y = y , x
    
  • 但是,在切片中,冒號的行為類似于二元運算符稠腊,并且在每一側都應具有相等的數(shù)量(將其視為優(yōu)先級最低的運算符)躁染。在擴展切片中,兩個冒號必須應用相同的間距架忌。例外:省略slice參數(shù)時吞彤,將省略空格。

    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[1: 9], ham[1 :9], ham[1:9 :3]
    ham[lower : : upper]
    ham[ : upper]
    
  • 在左圓括號之前叹放,該圓括號開始一個函數(shù)調用的參數(shù)列表:

    Yes:

    spam(1)
    

    No:

    spam (1)
    
  • 在左方括號之前立即開始索引或切片:

    Yes:

    dct['key'] = lst[index]
    

    No:

    dct ['key'] = lst [index]
    
  • 賦值(或其他)運算符周圍有多個空格饰恕,以使其與另一個對齊。

    Yes:

    x = 1
    y = 2
    long_variable = 3
    

    No:

    x             = 1
    y             = 2
    long_variable = 3
    

其他建議

  • 避免在任何地方拖尾空格井仰。因為它通常是不可見的埋嵌,所以可能會造成混淆:例如,反斜杠后跟一個空格和一個換行符不算作行繼續(xù)標記糕档。一些編輯器沒有保留它莉恼,并且許多項目(例如CPython本身)都具有拒絕它的預提交鉤子。

  • 總是圍繞這些二元運算符在任一側的單個空間:賦值(=)速那,增量賦值(+=俐银,-=等),比較(==端仰,<捶惜,>!=荔烧,<>吱七,<=>=鹤竭,in踊餐,not inis臀稚,is not)吝岭,布爾值(andor吧寺,not)窜管。

  • 如果使用優(yōu)先級不同的運算符,請考慮在優(yōu)先級最低的運算符周圍添加空格稚机。使用您自己的判斷幕帆;但是,永遠不要使用一個以上的空間赖条,并且在二進制運算符的兩邊總是具有相同數(shù)量的空白失乾。

    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)
    
  • =當用于指示關鍵字參數(shù)或默認參數(shù)值時常熙,請勿在符號周圍使用空格。

    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)
    
  • 函數(shù)注釋應使用冒號的常規(guī)規(guī)則仗扬,并且->如果有的話症概,箭頭周圍總是有空格。(有關功能注釋的更多信息早芭,請參見下面的功能注釋宜雀。)

    Yes:

    def munge(input: AnyStr): ...
    def munge() -> AnyStr: ...
    

    No:

    def munge(input:AnyStr): ...
    def munge()->PosInt: ...
    
  • 將參數(shù)注釋與默認值組合時治宣,請在=符號周圍使用空格(但僅適用于同時具有注釋和默認值的參數(shù))纱皆。

    Yes:

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

    No:

    def munge(input: AnyStr=None): ...
    def munge(input: AnyStr, limit = 1000): ...
    
  • 通常不建議使用復合語句(同一行上的多個語句)南蓬。

    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的小主體放在同一行上是可以的,但對于多子句語句則永遠不要這樣做语盈。也要避免折疊這么長的線舱馅!

    Rather not:

    if foo == 'blah': do_blah_thing()
    for x in lst: total += x
    while t < 10: t = delay()
    

    Definitely not:

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

何時使用尾隨逗號

尾部的逗號通常是可選的,但當組成一個元素的元組時它們是必需的(并且在Python 2中刀荒,它們具有該print語句的語義)代嗤。為了清楚起見,建議將后者用(技術上多余的)括號括起來缠借。

Yes:

FILES = ('setup.cfg',)

OK干毅,但是令人困惑:

FILES = 'setup.cfg',

如果結尾的逗號多余,則在使用版本控制系統(tǒng)時泼返,當值硝逢,參數(shù)或導入項的列表預計會隨著時間擴展時,它們通常會很有用绅喉。模式是將每個值(等)單獨放在一行上渠鸽,始終添加尾隨逗號,并在下一行上添加右括號/括號/花括號柴罐。但是徽缚,在與結束定界符相同的行上使用尾隨逗號是沒有意義的(在上述單例元組的情況下除外)。

Yes:

FILES = [
    'setup.cfg',
    'tox.ini',
    ]
initialize(FILES,
           error=True,
           )

No:

FILES = ['setup.cfg', 'tox.ini',]
initialize(FILES, error=True,)

注釋

與代碼矛盾的注釋比沒有注釋更糟糕革屠。當代碼更改時猎拨,始終優(yōu)先考慮最新注釋!

注釋應為完整句子屠阻。如果注釋是短語或句子,則除非注釋是以小寫字母開頭的標識符额各,否則其第一個單詞應大寫(切勿更改標識符的大小寫9酢)。

如果注釋簡短虾啦,則可以省略結尾的句點麻诀。整體注釋通常由一個或多個完整句子組成的段落組成痕寓,每個句子都應以句點結尾。

句子結尾后應使用兩個空格蝇闭。

塊注釋

塊注釋通常適用于其后的一些(或全部)代碼呻率,并且縮進到與該代碼相同的級別。塊注釋的每一行都以#和開頭(除非注釋內的文本是縮進的)呻引。

塊注釋中的段落由包含單個的行分隔#礼仗。

內聯(lián)注釋

謹慎使用內聯(lián)注釋。

內聯(lián)注釋是與語句在同一行上的注釋逻悠。內聯(lián)注釋應與該語句至少分隔兩個空格元践。它們應以#和單個空格開頭。

內聯(lián)注釋是不必要的童谒,并且如果它們表明顯而易見单旁,則實際上會分散注意力。

不要這樣做:

x = x + 1                 # Increment x

但是有時候饥伊,這很有用:

x = x + 1                 # Compensate for border

文檔字符串

PEP 257中象浑,編寫好的文檔字符串(也稱為“文檔字符串”)的約定很好。

  • 為所有公共模塊琅豆,函數(shù)愉豺,類和方法編寫文檔字符串。對于非公共方法趋距,文檔字符串不是必需的粒氧,但是您應該具有描述該方法功能的注釋。該注釋應出現(xiàn)在該def行之后节腐。

  • PEP 257描述了良好的文檔字符串約定外盯。請注意,最重要的是"""翼雀,以多行docstring結尾的本身應位于一行上饱苟,例如:

    """Return a foobang
    
    Optional plotz says to frobnicate the bizbaz first.
    """
    
  • 對于一個內襯文檔字符串,請使結尾處保持"""同一行狼渊。

命名約定

Python庫的命名約定有些混亂箱熬,因此我們永遠都無法做到完全一致。盡管如此狈邑,這是當前推薦的命名標準城须。新的模塊和軟件包(包括第三方框架)應按照這些標準編寫,但是如果現(xiàn)有庫具有不同的樣式米苹,則首選內部一致性糕伐。

首要原則

對于用戶而言,作為API公共部分可見的名稱應遵循反映用法而不是實現(xiàn)的約定蘸嘶。

描述性:命名樣式

有很多不同的命名樣式良瞧。能夠獨立于它們的用途來識別正在使用的命名樣式陪汽。

通常區(qū)分以下命名樣式:

  • b (單個小寫字母)
  • B (單個大寫字母)
  • lowercase
  • lower_case_with_underscores
  • UPPERCASE
  • UPPER_CASE_WITH_UNDERSCORES
  • CapitalizedWords(或CapWords,CamelCase 5和StudlyCaps)
  • mixedCase (與大小寫字母的首字母小寫字母不同H祢恰)
  • Capitalized_Words_With_Underscores (丑陋V吭)

注意:

在CapWords中使用縮寫詞時,將所有縮寫詞大寫赞庶。因此HTTPServerErrorHttpServerError更好训挡。

還有一種使用短的唯一前綴將相關名稱組合在一起的樣式。這在Python中使用不多尘执,但是為了完整起見提到它舍哄。例如,os.stat()函數(shù)返回一個元組誊锭,其項目通常有類似的名字st_mode表悬,st_sizest_mtime等等丧靡。(這樣做是為了強調與POSIX系統(tǒng)調用結構的字段的對應關系蟆沫,這有助于程序員熟悉該結構。)

X11庫將前導X用于其所有公共功能温治。在Python中饭庞,這種樣式通常被認為是不必要的,因為屬性和方法名稱以對象為前綴熬荆,函數(shù)名稱以模塊名作為前綴舟山。

此外,還可以識別出以下使用前劃線或后劃線的特殊形式(通陈笨遥可以將它們與任何大小寫慣例結合使用):

  • _single_leading_underscore:“內部使用”指示器累盗。例如from M import *,不導入名稱以下劃線開頭的對象突琳。

  • single_trailing_underscore_:按慣例用于避免與Python關鍵字沖突若债,例如:

    Tkinter.Toplevel(master, class_='ClassName')
    
  • __double_leading_underscore:在命名類屬性時,調用名稱修飾(在類FooBar內部拆融,__boo變?yōu)?code>_FooBar__boo;見下文)蠢琳。

  • __double_leading_and_trailing_underscore__:位于用戶控制的名稱空間中的“魔術”對象或屬性。例如__init__镜豹,__import____file__傲须。請勿發(fā)明此類名稱;僅按記錄使用它們趟脂。

說明性:命名約定

避免使用的名稱

切勿將字符“ l”(小寫字母l)泰讽,“ O”(大寫字母o)或“ I”(大寫字母i)用作單個字符變量名稱。

在某些字體中,這些字符與數(shù)字1和零沒有區(qū)別菇绵。當嘗試使用“ l”時,請改用“ L”镇眷。

軟件包和模塊名稱

模塊應使用簡短的全小寫名稱咬最。如果模塊名稱可以提高可讀性,則可以在模塊名稱中使用下劃線欠动。盡管不鼓勵使用下劃線永乌,但Python軟件包也應使用短的全小寫名稱。

當用C或C ++編寫的擴展模塊具有隨附的Python模塊提供更高級別(例如具伍,面向對象)的接口時翅雏,C / C ++模塊具有一個下劃線(例如_socket)。

類名

類名通常應使用CapWords約定人芽。

在接口被記錄并主要用作可調用函數(shù)的情況下望几,可以代替使用函數(shù)的命名約定。

請注意萤厅,內置名稱有一個單獨的約定:大多數(shù)內置名稱是單個單詞(或兩個單詞一起)橄抹,而CapWords約定僅用于異常名稱和內置常量。

類型變量名稱

在PEP 484引入的類型變量的名稱通常應當使用CapWords短名稱:T惕味,AnyStr楼誓,Num。建議在用于聲明協(xié)變或反變行為的變量中添加后綴_co_contra變量名挥。例子

from typing import TypeVar

VT_co = TypeVar('VT_co', covariant=True)
KT_contra = TypeVar('KT_contra', contravariant=True)

異常名稱

因為異常應該是類疟羹,所以此處使用類命名約定。但是禀倔,您應該在異常名稱上使用后綴“ Error”(如果異常實際上是一個錯誤)榄融。

全局變量名

(我們希望這些變量只能在一個模塊內使用。)約定與函數(shù)的約定大致相同蹋艺。

設計的模塊from M import *應使用__all__防止導出全局變量的機制剃袍,或使用較早的約定在此類全局變量前加下劃線(您可能需要這樣做以指示這些全局變量是“模塊非公共”)。

功能名稱

函數(shù)名稱應小寫捎谨,必要時用下劃線分隔單詞民效,以提高可讀性。

僅在已經(jīng)是主流樣式(例如threading.py)的上下文中才允許使用mixedCase涛救,以保持向后兼容性畏邢。

函數(shù)和方法參數(shù)

始終使用self實例方法的第一個參數(shù)。

始終使用cls類方法的第一個參數(shù)检吆。

如果函數(shù)參數(shù)的名稱與保留關鍵字發(fā)生沖突舒萎,通常最好在其后附加一個下劃線,而不要使用縮寫或拼寫錯誤蹭沛。因此class_clss更好臂寝。(也許更好的辦法是使用同義詞來避免此類沖突章鲤。)

方法名稱和實例變量

函數(shù)命名規(guī)則:小寫字母,單詞以下劃線分隔咆贬,以提高可讀性败徊。

僅對非公共方法和實例變量使用前導下劃線。

為避免名稱與子類發(fā)生沖突掏缎,請使用兩個下劃線來調用Python的名稱處理規(guī)則皱蹦。

Python用類名來修飾這些名稱:如果Foo類具有一個名為的屬性__a,則不能通過來訪問它Foo.__a眷蜈。(堅持的用戶仍然可以通過調用Foo._Foo__a獲得訪問權限)通常沪哺,雙引號下劃線僅應用于避免名稱與設計為子類的類中的屬性發(fā)生沖突。

注意:關于__name的使用存在一些爭議

常數(shù)

常量通常在模塊級別定義酌儒,并以所有大寫字母書寫辜妓,并用下劃線分隔單詞。示例包括MAX_OVERFLOWTOTAL今豆。

為繼承而設計

始終確定類的方法和實例變量(統(tǒng)稱為“屬性”)應該是公共的還是非公共的嫌拣。如有疑問,請選擇非公開呆躲;稍后將其公開比使公共屬性不公開要容易异逐。

公共屬性是您期望班級中不相關的客戶端使用的屬性,并承諾避免向后不兼容的更改插掂。非公開屬性是指不打算由第三方使用的屬性灰瞻;您不保證非公共屬性不會更改甚至被刪除。

我們在這里不使用術語“私有”辅甥,因為在Python中沒有任何屬性是真正私有的(通常沒有不必要的工作量)酝润。

另一類屬性是屬于“子類API”(在其他語言中通常稱為“受保護”)的那些屬性。某些類被設計為可繼承的璃弄,以擴展或修改類行為的各個方面要销。在設計這樣的類時,請務必明確決定哪些屬性是公共屬性夏块,哪些是子類API的一部分疏咐,哪些屬性僅真正由您的基類使用。

考慮到這一點脐供,以下是Python準則:

  • 公共屬性不應包含前導下劃線浑塞。

  • 如果您的公共屬性名稱與保留關鍵字沖突,請在屬性名稱后附加一個下劃線政己。這優(yōu)于縮寫或拼寫錯誤酌壕。(但是,盡管有此規(guī)則,對于已知為類的任何變量或參數(shù)卵牍,尤其是類方法的第一個參數(shù)果港,“ cls”是首選的拼寫。)

    注1:有關類方法糊昙,請參見上面的參數(shù)名稱建議京腥。

  • 如果您的類打算被子類化,并且您具有不希望使用子類的屬性溅蛉,請考慮使用雙引號和下劃線來命名它們。這將調用Python的名稱修改算法他宛,其中將類的名稱修改為屬性名稱船侧。這有助于避免屬性名稱沖突,如果子類無意中包含具有相同名稱的屬性厅各。

公共和內部接口

任何向后兼容性保證都僅適用于公共接口镜撩。因此,重要的是用戶能夠清楚地區(qū)分公共接口和內部接口队塘。

除非文檔明確聲明它們是臨時接口或內部接口不受通常的向后兼容性保證袁梗,否則文檔化接口被視為公共接口。所有未記錄的接口都應假定為內部接口憔古。

為了更好地支持自省遮怜,模塊應該使用__all__屬性在其公共API中顯式聲明名稱。設置__all__為空列表表示該模塊沒有公共API鸿市。

即使__all__設置適當锯梁,內部接口(包,模塊焰情,類陌凳,函數(shù),屬性或其他名稱)仍應以單個下劃線作為前綴内舟。

如果任何包含名稱空間(包合敦,模塊或類)的內部接口都被視為內部接口,則該接口也被視為內部接口验游。

導入的名稱應始終被視為實現(xiàn)細節(jié)充岛。其他模塊不得依賴對此類導入名稱的間接訪問,除非它們是包含模塊的API中明確記錄的一部分批狱,例如os.path__init__暴露了子模塊功能的軟件包模塊裸准。

編程建議

  • 應該以不損害Python其他實現(xiàn)(PyPy,Jython赔硫,IronPython炒俱,Cython,Psyco等)的方式編寫代碼。

  • 與單例(如None)的比較應始終使用is或進行is not权悟,絕不能使用相等運算符進行砸王。

    另外,當心if x您的意思if x is not None峦阁,例如當測試是否將默認設置為None的變量或參數(shù)設置為其他值時谦铃,請當心編寫。另一個值可能具有在布爾上下文中可能為false的類型(例如容器)榔昔!

  • 使用is not運算符而不是not ... is驹闰。盡管兩個表達式在功能上相同,但前者更易讀和首選撒会。

    Yes:

    if foo is not None:
    

    No:

    if not foo is None:
    
  • 當有比較豐富排序執(zhí)行的操作嘹朗,最好是實現(xiàn)所有六個操作(__eq____ne__诵肛,__lt__屹培,__le____gt__怔檩,__ge__)而不是依靠其他代碼褪秀,只行使特定的比較。

    為了最大程度地減少工作量薛训,functools.total_ordering()裝飾器提供了一種生成缺少的比較方法的工具媒吗。

  • 始終使用def語句而不是將lambda表達式直接綁定到標識符的賦值語句。

    Yes:

    def f(x): return 2*x
    

    No:

    f = lambda x: 2*x
    

    第一種形式表示結果函數(shù)對象的名稱專門為“ f”乙埃,而不是通用的“ <lambda>”蝴猪。通常,這對于回溯和字符串表示形式更為有用膊爪。使用賦值語句消除了lambda表達式相對于顯式def語句(即自阱,可以將其嵌入較大的表達式中)提供的唯一好處。

  • Exception而不是從BaseException派生異常

    類命名約定在此處適用米酬,但是如果異常是錯誤沛豌,則應在異常類中添加后綴“ Error”。

  • 在Python 2中引發(fā)異常時赃额,請使用加派,raise ValueError('message')而不要使用舊的形式 raise ValueError, 'message'

    后一種形式不是合法的Python 3語法跳芳。

    使用括號的形式還意味著芍锦,當異常參數(shù)很長或包含字符串格式時,由于包含括號飞盆,因此不需要使用行繼續(xù)符娄琉。

  • 捕獲異常時次乓,請盡可能提及特定的異常,而不要使用空except:子句孽水。

    例如票腰,使用:

    try:
        import platform_specific_module
    except ImportError:
        platform_specific_module = None
    

    except:子句將捕獲SystemExit和KeyboardInterrupt異常,這使得使用Control-C中斷程序更加困難女气,并且可以掩蓋其他問題空免。如果要捕獲所有表明程序錯誤的異常淌铐,請使用except Exception:

    一個好的經(jīng)驗法則是將空的'except'子句的使用限制為兩種情況:

    1. 如果異常處理程序將打印輸出或記錄回溯;至少用戶會意識到發(fā)生了錯誤敦第。
    2. 如果代碼需要做一些清理工作秧饮,但是讓異常向上傳播raise阴颖。try...finally可能是處理這種情況的更好方法峦甩。
  • 將捕獲的異常綁定到名稱時嘱朽,最好使用Python 2.6中添加的顯式名稱綁定語法:

    try:
        process_data()
    except Exception as exc:
        raise DataProcessingFailedError(str(exc))
    

    這是Python 3中唯一支持的語法,并且避免了與較早的基于逗號的語法相關的歧義問題瘩将。

  • 捕獲操作系統(tǒng)錯誤時,與errno值的自省相比凹耙,更喜歡Python 3.3中引入的顯式異常層次結構姿现。

  • 此外,對于所有try / except子句肖抱,請將try子句限制為所需的絕對最小數(shù)量的代碼备典。同樣,這避免了掩蓋錯誤意述。

    Yes:

    try:
        value = collection[key]
    except KeyError:
        return key_not_found(key)
    else:
        return handle_value(value)
    

    No:

    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語句也是可以接受的拌屏。

  • 每當他們執(zhí)行除獲取和釋放資源以外的其他操作時,都應通過單獨的函數(shù)或方法來調用上下文管理器术荤。例如:

    Yes:

    with conn.begin_transaction():
        do_stuff_in_transaction(conn)
    

    No:

    with conn:
        do_stuff_in_transaction(conn)
    

    后面的示例沒有提供任何信息來指示__enter____exit__方法在事務處理后關閉連接以外的操作倚喂。在這種情況下,暴露很重要瓣戚。

  • 在返回語句中保持一致端圈。函數(shù)中的所有return語句應該返回一個表達式,或者都不返回子库。如果任何return語句返回一個表達式舱权,則不返回任何值的任何return語句return None都應將其顯式聲明為,并且在函數(shù)末尾(如果可以到達)應存在一個顯式return語句仑嗅。

    Yes:

    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)
    

    No:

    def foo(x):
        if x >= 0:
            return math.sqrt(x)
    
    def bar(x):
        if x < 0:
            return
        return math.sqrt(x)
    
  • 使用字符串方法而不是字符串模塊宴倍。

    字符串方法總是更快张症,并且與unicode字符串共享相同的API

  • 使用''.startswith()''.endswith()代替字符串切片來檢查前綴或后綴。

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

    在檢查對象是否為字符串時,請記住它也可能是unicode字符串恭理!在Python 2中拯辙,str和unicode具有一個公共基類basestring,因此您可以執(zhí)行以下操作:

    if isinstance(obj, basestring):
    

    請注意颜价,在Python 3中涯保,unicodebasestring不再存在(只有str),并且bytes對象不再是字符串的一種(而是整數(shù)序列)

  • 對于序列(字符串周伦,列表夕春,元組),請使用空序列為假的事實:

    Yes:

    if not seq:
    if seq:
    

    No:

    if len(seq):
    if not len(seq):
    
  • 不要寫依賴大量尾隨空格的字符串文字专挪。這種尾隨的空格在視覺上是無法區(qū)分的及志,某些編輯器(或更近期的reindent.py)將對其進行修剪。

  • 不要使用==以下方法將布爾值與True或False進行比較:

    Yes:

    if greeting:
    

    No:

    if greeting == True:
    

    Worse:

    if greeting is True:
    

參考

PEP8

介紹

本文檔給出了Python代碼的編碼約定寨腔。

該樣式指南會隨著時間的流逝而發(fā)展速侈,因為會發(fā)現(xiàn)其他約定,而過去的約定由于語言本身的更改而變得過時迫卢。

許多項目都有自己的編碼風格準則倚搬。如有任何沖突,此類項目特定的指南優(yōu)先于該項目乾蛤。

一致性

代碼的讀取次數(shù)比編寫的次數(shù)多每界。此處提供的指南旨在提高代碼的可讀性,并使其在各種Python代碼中保持一致家卖。正如PEP 20所說眨层,“可讀性至關重要”。

代碼布局

縮進

每個縮進級別使用4個空格上荡。

續(xù)行可以和括號相鄰元素對齊谐岁,或使用懸掛式縮進

Yes:

# Aligned with opening delimiter.
foo = long_function_name(var_one, var_two,
                         var_three, var_four)

# More indentation included to distinguish this from the rest.
def long_function_name(
        var_one, var_two, var_three,
        var_four):
    print(var_one)

# Hanging indents should add a level.
foo = long_function_name(
    var_one, var_two,
    var_three, var_four)

No:

# Arguments on first line forbidden when not using vertical alignment.
foo = long_function_name(var_one, var_two,
    var_three, var_four)

# Further indentation required as indentation is not distinguishable.
def long_function_name(
    var_one, var_two, var_three,
    var_four):
    print(var_one)

對于連續(xù)行,4空格規(guī)則是可選的榛臼。

可選的:

# Hanging indents *may* be indented to other than 4 spaces.
foo = long_function_name(
  var_one, var_two,
  var_three, var_four)

if語句的條件部分足夠長而要求將其寫成多行時伊佃,可接受的選項包括但不限于:

# No extra indentation.
if (this_is_one_thing and
    that_is_another_thing):
    do_something()

# Add a comment, which will provide some distinction in editors
# supporting syntax highlighting.
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()

(另請參見下面有關在二進制運算符之前還是之后中斷的討論。)

多行構造的右花括號/括號/括號可以在列表最后一行的第一個非空白字符下對齊沛善,如下所示:

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',
)

制表符或空格?

空格是首選的縮進方法金刁。

不推薦使用制表符

Python 3不允許混合使用制表符和空格進行縮進帅涂。

縮進制表符和空格混合在一起的Python 2代碼應轉換為使用空格议薪。

最大長度

限制所有行最多79個字符。

通過限制所需的編輯器窗口寬度媳友,可以并排打開多個文件斯议,并且在使用在相鄰列中顯示兩個版本的代碼查看工具時,效果很好醇锚。

大多數(shù)工具中的默認包裝會破壞代碼的視覺結構哼御,使其更難以理解。選擇這些限制是為了避免在窗口寬度設置為80的編輯器中進行換行焊唬,即使在換行時該工具在最后一列中放置了標志符號也是如此恋昼。某些基于Web的工具可能根本不提供動態(tài)換行。

一些團隊喜歡更長的線長赶促。對于專門或主要由可以在此問題上達成協(xié)議的團隊維護的代碼液肌,可以將標稱行長度從80個字符增加到100個字符(有效地將最大長度增加到99個字符)。

換行的首選方法是在括號鸥滨,方括號和花括號內使用Python的隱含行連續(xù)性嗦哆。通過將表達式包裝在括號中,可以將長行分成多行婿滓。應優(yōu)先使用這些老速,而不是使用反斜杠進行行連續(xù)。

有時反斜杠仍然適用空幻。例如,長的多with語句不能使用隱式連續(xù)容客,因此可以使用反斜杠:

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

確保適當縮進續(xù)行秕铛。

換行符應該在二進制運算符之前還是之后?

幾十年來缩挑,推薦的樣式是二元運算符之后換行但两。但這會以兩種方式損害可讀性:運算符趨向于分散在屏幕上的不同列上,并且每個運算符都從其操作數(shù)移至上一行供置。在這里谨湘,眼睛必須做額外的工作才能分辨出添加了哪些項目和減去了哪些項目:

# No: operators sit far away from their operands
income = (gross_wages +
          taxable_interest +
          (dividends - qualified_dividends) -
          ira_deduction -
          student_loan_interest)

為了解決此可讀性問題,數(shù)學家及其發(fā)布者遵循相反的約定芥丧。Donald Knuth在他的“計算機和排版”系列中解釋了傳統(tǒng)規(guī)則:

“盡管段落中的公式總是在二進制運算和關系之后中斷紧阔,但顯示的公式總是在二進制運算和關系之前中斷”

遵循數(shù)學的傳統(tǒng),通常會導致代碼更具可讀性:

# Yes: easy to match operators with operands
income = (gross_wages
          + taxable_interest
          + (dividends - qualified_dividends)
          - ira_deduction
          - student_loan_interest)

在Python代碼中续担,只要約定在本地是一致的擅耽,就可以在二進制運算符之前或之后中斷。對于新代碼物遇,建議使用Knuth的樣式乖仇。

空行

用兩個空行分隔頂級函數(shù)和類定義憾儒。

類內的方法定義由單個空白行分隔。

多余的空白行可以(分別)用于分隔相關功能組乃沙。

在函數(shù)中使用空白行起趾,以區(qū)分各個邏輯部分。

源文件編碼

核心Python發(fā)行版中的代碼應始終使用UTF-8(或Python 2中的ASCII)警儒。

使用ASCII(在Python 2中)或UTF-8(在Python 3中)的文件不應具有編碼聲明训裆。

在標準庫中,非默認編碼僅應用于測試目的冷蚂,或者在注釋或文檔字符串需要提及包含非ASCII字符的作者姓名時缭保;否則,使用\x蝙茶,\u艺骂,\U,或\N逃逸是包含在字符串非ASCII數(shù)據(jù)的首選方式隆夯。

對于Python 3.0及更高版本钳恕,標準庫規(guī)定了以下策略(請參閱PEP 3131):Python標準庫中的所有標識符務必使用純ASCII標識符,并且在可行的情況下應使用英文單詞(在許多情況下蹄衷,縮寫和技術使用的術語不是英語)忧额。此外,字符串文字和注釋也必須使用ASCII愧口。唯一的例外是(a)測試非ASCII功能的測試用例睦番,以及(b)作者的姓名。名稱不基于拉丁字母的作者必須提供其姓名的拉丁音譯耍属。

鼓勵具有全球受眾的開源項目采取類似的政策托嚣。

Import

  • 導入通常應放在單獨的行上,例如:

    Yes:

    import os
    import sys
    

    No:

    import os, sys
    

    可以這樣寫:

    from subprocess import Popen, PIPE
    
  • 導入總是放在文件的頂部厚骗,緊隨任何模塊注釋和文檔字符串之后示启,以及模塊全局變量和常量之前。

    導入應按以下順序分組:

    1. 標準庫導入
    2. 相關第三方進口
    3. 本地應用程序/特定于庫的導入

    您應該在每組導入之間放置一個空白行领舰。

  • 推薦使用絕對導入夫嗓,因為如果導入系統(tǒng)配置不正確(例如,程序包中的目錄最終位于時sys.path)冲秽,則它們通常更易于閱讀舍咖,并且通常表現(xiàn)得更好(或至少會提供更好的錯誤消息):

    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
    

    并使用myclass.MyClassfoo.bar.yourclass.YourClass

  • from <module> import *應避免使用通配符導入飞涂,因為通配符不能弄清楚名稱空間中存在哪些名稱旦部,這會混淆閱讀器和許多自動化工具。通配符導入有一個合理的用例较店,它是將內部接口重新發(fā)布為公共API的一部分

    以這種方式重新發(fā)布名稱時士八,以下有關公共和內部接口的準則仍然適用。

模塊級dunder名稱

模塊級“dunders”(即名稱具有兩個前下劃線和兩個后下劃線)梁呈,例如__all__婚度,__author____version__等應被放置在模塊文檔字符串之后官卡,但在任何導入語句除了 from __future__ import之后蝗茁。Python要求future import必須在模塊中出現(xiàn)在除文檔字符串以外的任何其他代碼之前。

例如:

"""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中寻咒,單引號字符串和雙引號字符串是相同的哮翘。本PEP對此不做任何建議。選擇一條規(guī)則并堅持下去毛秘。但是饭寺,當字符串包含單引號或雙引號字符時,請使用另一個以避免在字符串中使用反斜杠叫挟。它提高了可讀性艰匙。

對于三引號字符串,請始終使用雙引號字符以與PEP 257中的docstring約定一致霞揉。

表達式和語句中的空格

一些習慣

在以下情況下旬薯,請避免使用多余的空格:

  • 緊靠在括號晰骑,方括號或大括號內:

    Yes:

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

    No:

    spam( ham[ 1 ], { eggs: 2 } )
    
  • 在尾隨逗號和后面的右括號之間:

    Yes:

    foo = (0,)
    

    No:

    bar = (0, )
    
  • 在逗號适秩,分號或冒號之前:

    Yes:

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

    No:

    if x == 4 : print x , y ; x , y = y , x
    
  • 但是,在切片中硕舆,冒號的行為類似于二元運算符秽荞,并且在每一側都應具有相等的數(shù)量(將其視為優(yōu)先級最低的運算符)。在擴展切片中抚官,兩個冒號必須應用相同的間距扬跋。例外:省略slice參數(shù)時,將省略空格凌节。

    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[1: 9], ham[1 :9], ham[1:9 :3]
    ham[lower : : upper]
    ham[ : upper]
    
  • 在左圓括號之前钦听,該圓括號開始一個函數(shù)調用的參數(shù)列表:

    Yes:

    spam(1)
    

    No:

    spam (1)
    
  • 在左方括號之前立即開始索引或切片:

    Yes:

    dct['key'] = lst[index]
    

    No:

    dct ['key'] = lst [index]
    
  • 賦值(或其他)運算符周圍有多個空格洒试,以使其與另一個對齊。

    Yes:

    x = 1
    y = 2
    long_variable = 3
    

    No:

    x             = 1
    y             = 2
    long_variable = 3
    

其他建議

  • 避免在任何地方拖尾空格朴上。因為它通常是不可見的垒棋,所以可能會造成混淆:例如,反斜杠后跟一個空格和一個換行符不算作行繼續(xù)標記痪宰。一些編輯器沒有保留它叼架,并且許多項目(例如CPython本身)都具有拒絕它的預提交鉤子。

  • 總是圍繞這些二元運算符在任一側的單個空間:賦值(=)衣撬,增量賦值(+=乖订,-=等),比較(==具练,<乍构,>!=靠粪,<>蜡吧,<=>=占键,in昔善,not inis畔乙,is not)君仆,布爾值(andor牲距,not)返咱。

  • 如果使用優(yōu)先級不同的運算符,請考慮在優(yōu)先級最低的運算符周圍添加空格牍鞠。使用您自己的判斷咖摹;但是,永遠不要使用一個以上的空間难述,并且在二進制運算符的兩邊總是具有相同數(shù)量的空白萤晴。

    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)
    
  • =當用于指示關鍵字參數(shù)或默認參數(shù)值時,請勿在符號周圍使用空格胁后。

    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)
    
  • 函數(shù)注釋應使用冒號的常規(guī)規(guī)則店读,并且->如果有的話,箭頭周圍總是有空格攀芯。(有關功能注釋的更多信息屯断,請參見下面的功能注釋。)

    Yes:

    def munge(input: AnyStr): ...
    def munge() -> AnyStr: ...
    

    No:

    def munge(input:AnyStr): ...
    def munge()->PosInt: ...
    
  • 將參數(shù)注釋與默認值組合時,請在=符號周圍使用空格(但僅適用于同時具有注釋和默認值的參數(shù))殖演。

    Yes:

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

    No:

    def munge(input: AnyStr=None): ...
    def munge(input: AnyStr, limit = 1000): ...
    
  • 通常不建議使用復合語句(同一行上的多個語句)氧秘。

    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的小主體放在同一行上是可以的,但對于多子句語句則永遠不要這樣做趴久。也要避免折疊這么長的線敏储!

    Rather not:

    if foo == 'blah': do_blah_thing()
    for x in lst: total += x
    while t < 10: t = delay()
    

    Definitely not:

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

何時使用尾隨逗號

尾部的逗號通常是可選的,但當組成一個元素的元組時它們是必需的(并且在Python 2中朋鞍,它們具有該print語句的語義)已添。為了清楚起見,建議將后者用(技術上多余的)括號括起來滥酥。

Yes:

FILES = ('setup.cfg',)

OK更舞,但是令人困惑:

FILES = 'setup.cfg',

如果結尾的逗號多余,則在使用版本控制系統(tǒng)時坎吻,當值缆蝉,參數(shù)或導入項的列表預計會隨著時間擴展時,它們通常會很有用瘦真。模式是將每個值(等)單獨放在一行上刊头,始終添加尾隨逗號,并在下一行上添加右括號/括號/花括號诸尽。但是原杂,在與結束定界符相同的行上使用尾隨逗號是沒有意義的(在上述單例元組的情況下除外)。

Yes:

FILES = [
    'setup.cfg',
    'tox.ini',
    ]
initialize(FILES,
           error=True,
           )

No:

FILES = ['setup.cfg', 'tox.ini',]
initialize(FILES, error=True,)

注釋

與代碼矛盾的注釋比沒有注釋更糟糕您机。當代碼更改時穿肄,始終優(yōu)先考慮最新注釋!

注釋應為完整句子际看。如果注釋是短語或句子咸产,則除非注釋是以小寫字母開頭的標識符,否則其第一個單詞應大寫(切勿更改標識符的大小寫V倜觥)脑溢。

如果注釋簡短,則可以省略結尾的句點赖欣。整體注釋通常由一個或多個完整句子組成的段落組成屑彻,每個句子都應以句點結尾。

句子結尾后應使用兩個空格畏鼓。

塊注釋

塊注釋通常適用于其后的一些(或全部)代碼酱酬,并且縮進到與該代碼相同的級別壶谒。塊注釋的每一行都以#和開頭(除非注釋內的文本是縮進的)云矫。

塊注釋中的段落由包含單個的行分隔#

內聯(lián)注釋

謹慎使用內聯(lián)注釋汗菜。

內聯(lián)注釋是與語句在同一行上的注釋让禀。內聯(lián)注釋應與該語句至少分隔兩個空格挑社。它們應以#和單個空格開頭。

內聯(lián)注釋是不必要的巡揍,并且如果它們表明顯而易見痛阻,則實際上會分散注意力。

不要這樣做:

x = x + 1                 # Increment x

但是有時候腮敌,這很有用:

x = x + 1                 # Compensate for border

文檔字符串

PEP 257中阱当,編寫好的文檔字符串(也稱為“文檔字符串”)的約定很好。

  • 為所有公共模塊糜工,函數(shù)弊添,類和方法編寫文檔字符串。對于非公共方法捌木,文檔字符串不是必需的油坝,但是您應該具有描述該方法功能的注釋。該注釋應出現(xiàn)在該def行之后刨裆。

  • PEP 257描述了良好的文檔字符串約定澈圈。請注意,最重要的是"""帆啃,以多行docstring結尾的本身應位于一行上瞬女,例如:

    """Return a foobang
    
    Optional plotz says to frobnicate the bizbaz first.
    """
    
  • 對于一個內襯文檔字符串,請使結尾處保持"""同一行努潘。

命名約定

Python庫的命名約定有些混亂拆魏,因此我們永遠都無法做到完全一致。盡管如此慈俯,這是當前推薦的命名標準渤刃。新的模塊和軟件包(包括第三方框架)應按照這些標準編寫,但是如果現(xiàn)有庫具有不同的樣式贴膘,則首選內部一致性卖子。

首要原則

對于用戶而言,作為API公共部分可見的名稱應遵循反映用法而不是實現(xiàn)的約定刑峡。

描述性:命名樣式

有很多不同的命名樣式洋闽。能夠獨立于它們的用途來識別正在使用的命名樣式。

通常區(qū)分以下命名樣式:

  • b (單個小寫字母)
  • B (單個大寫字母)
  • lowercase
  • lower_case_with_underscores
  • UPPERCASE
  • UPPER_CASE_WITH_UNDERSCORES
  • CapitalizedWords(或CapWords突梦,CamelCase 5和StudlyCaps)
  • mixedCase (與大小寫字母的首字母小寫字母不同=刖恕)
  • Capitalized_Words_With_Underscores (丑陋!)

注意:

在CapWords中使用縮寫詞時宫患,將所有縮寫詞大寫刊懈。因此HTTPServerErrorHttpServerError更好。

還有一種使用短的唯一前綴將相關名稱組合在一起的樣式。這在Python中使用不多虚汛,但是為了完整起見提到它匾浪。例如,os.stat()函數(shù)返回一個元組卷哩,其項目通常有類似的名字st_mode蛋辈,st_sizest_mtime等等将谊。(這樣做是為了強調與POSIX系統(tǒng)調用結構的字段的對應關系冷溶,這有助于程序員熟悉該結構。)

X11庫將前導X用于其所有公共功能尊浓。在Python中挂洛,這種樣式通常被認為是不必要的,因為屬性和方法名稱以對象為前綴眠砾,函數(shù)名稱以模塊名作為前綴虏劲。

此外,還可以識別出以下使用前劃線或后劃線的特殊形式(通嘲保可以將它們與任何大小寫慣例結合使用):

  • _single_leading_underscore:“內部使用”指示器柒巫。例如from M import *,不導入名稱以下劃線開頭的對象谷丸。

  • single_trailing_underscore_:按慣例用于避免與Python關鍵字沖突堡掏,例如:

    Tkinter.Toplevel(master, class_='ClassName')
    
  • __double_leading_underscore:在命名類屬性時,調用名稱修飾(在類FooBar內部,__boo變?yōu)?code>_FooBar__boo;見下文)。

  • __double_leading_and_trailing_underscore__:位于用戶控制的名稱空間中的“魔術”對象或屬性捂龄。例如__init____import____file__亭畜。請勿發(fā)明此類名稱;僅按記錄使用它們迎卤。

說明性:命名約定

避免使用的名稱

切勿將字符“ l”(小寫字母l)拴鸵,“ O”(大寫字母o)或“ I”(大寫字母i)用作單個字符變量名稱。

在某些字體中蜗搔,這些字符與數(shù)字1和零沒有區(qū)別劲藐。當嘗試使用“ l”時,請改用“ L”樟凄。

軟件包和模塊名稱

模塊應使用簡短的全小寫名稱聘芜。如果模塊名稱可以提高可讀性,則可以在模塊名稱中使用下劃線缝龄。盡管不鼓勵使用下劃線汰现,但Python軟件包也應使用短的全小寫名稱挂谍。

當用C或C ++編寫的擴展模塊具有隨附的Python模塊提供更高級別(例如,面向對象)的接口時服鹅,C / C ++模塊具有一個下劃線(例如_socket)。

類名

類名通常應使用CapWords約定百新。

在接口被記錄并主要用作可調用函數(shù)的情況下企软,可以代替使用函數(shù)的命名約定。

請注意饭望,內置名稱有一個單獨的約定:大多數(shù)內置名稱是單個單詞(或兩個單詞一起)仗哨,而CapWords約定僅用于異常名稱和內置常量。

類型變量名稱

在PEP 484引入的類型變量的名稱通常應當使用CapWords短名稱:T铅辞,AnyStr厌漂,Num。建議在用于聲明協(xié)變或反變行為的變量中添加后綴_co_contra變量斟珊。例子

from typing import TypeVar

VT_co = TypeVar('VT_co', covariant=True)
KT_contra = TypeVar('KT_contra', contravariant=True)

異常名稱

因為異常應該是類苇倡,所以此處使用類命名約定。但是囤踩,您應該在異常名稱上使用后綴“ Error”(如果異常實際上是一個錯誤)旨椒。

全局變量名

(我們希望這些變量只能在一個模塊內使用。)約定與函數(shù)的約定大致相同堵漱。

設計的模塊from M import *應使用__all__防止導出全局變量的機制综慎,或使用較早的約定在此類全局變量前加下劃線(您可能需要這樣做以指示這些全局變量是“模塊非公共”)。

功能名稱

函數(shù)名稱應小寫勤庐,必要時用下劃線分隔單詞示惊,以提高可讀性。

僅在已經(jīng)是主流樣式(例如threading.py)的上下文中才允許使用mixedCase愉镰,以保持向后兼容性米罚。

函數(shù)和方法參數(shù)

始終使用self實例方法的第一個參數(shù)。

始終使用cls類方法的第一個參數(shù)丈探。

如果函數(shù)參數(shù)的名稱與保留關鍵字發(fā)生沖突阔拳,通常最好在其后附加一個下劃線,而不要使用縮寫或拼寫錯誤类嗤。因此class_clss更好糊肠。(也許更好的辦法是使用同義詞來避免此類沖突。)

方法名稱和實例變量

函數(shù)命名規(guī)則:小寫字母遗锣,單詞以下劃線分隔货裹,以提高可讀性。

僅對非公共方法和實例變量使用前導下劃線精偿。

為避免名稱與子類發(fā)生沖突弧圆,請使用兩個下劃線來調用Python的名稱處理規(guī)則赋兵。

Python用類名來修飾這些名稱:如果Foo類具有一個名為的屬性__a,則不能通過來訪問它Foo.__a搔预。(堅持的用戶仍然可以通過調用Foo._Foo__a獲得訪問權限)通常霹期,雙引號下劃線僅應用于避免名稱與設計為子類的類中的屬性發(fā)生沖突。

注意:關于__name的使用存在一些爭議

常數(shù)

常量通常在模塊級別定義拯田,并以所有大寫字母書寫历造,并用下劃線分隔單詞。示例包括MAX_OVERFLOWTOTAL船庇。

為繼承而設計

始終確定類的方法和實例變量(統(tǒng)稱為“屬性”)應該是公共的還是非公共的吭产。如有疑問,請選擇非公開鸭轮;稍后將其公開比使公共屬性不公開要容易臣淤。

公共屬性是您期望班級中不相關的客戶端使用的屬性,并承諾避免向后不兼容的更改窃爷。非公開屬性是指不打算由第三方使用的屬性邑蒋;您不保證非公共屬性不會更改甚至被刪除。

我們在這里不使用術語“私有”按厘,因為在Python中沒有任何屬性是真正私有的(通常沒有不必要的工作量)寺董。

另一類屬性是屬于“子類API”(在其他語言中通常稱為“受保護”)的那些屬性。某些類被設計為可繼承的刻剥,以擴展或修改類行為的各個方面遮咖。在設計這樣的類時,請務必明確決定哪些屬性是公共屬性造虏,哪些是子類API的一部分御吞,哪些屬性僅真正由您的基類使用。

考慮到這一點漓藕,以下是Python準則:

  • 公共屬性不應包含前導下劃線陶珠。

  • 如果您的公共屬性名稱與保留關鍵字沖突,請在屬性名稱后附加一個下劃線享钞。這優(yōu)于縮寫或拼寫錯誤揍诽。(但是,盡管有此規(guī)則栗竖,對于已知為類的任何變量或參數(shù)暑脆,尤其是類方法的第一個參數(shù),“ cls”是首選的拼寫狐肢。)

    注1:有關類方法添吗,請參見上面的參數(shù)名稱建議。

  • 如果您的類打算被子類化份名,并且您具有不希望使用子類的屬性碟联,請考慮使用雙引號和下劃線來命名它們妓美。這將調用Python的名稱修改算法,其中將類的名稱修改為屬性名稱鲤孵。這有助于避免屬性名稱沖突壶栋,如果子類無意中包含具有相同名稱的屬性。

公共和內部接口

任何向后兼容性保證都僅適用于公共接口普监。因此贵试,重要的是用戶能夠清楚地區(qū)分公共接口和內部接口。

除非文檔明確聲明它們是臨時接口或內部接口不受通常的向后兼容性保證鹰椒,否則文檔化接口被視為公共接口锡移。所有未記錄的接口都應假定為內部接口呕童。

為了更好地支持自省漆际,模塊應該使用__all__屬性在其公共API中顯式聲明名稱。設置__all__為空列表表示該模塊沒有公共API夺饲。

即使__all__設置適當奸汇,內部接口(包,模塊往声,類擂找,函數(shù),屬性或其他名稱)仍應以單個下劃線作為前綴浩销。

如果任何包含名稱空間(包贯涎,模塊或類)的內部接口都被視為內部接口,則該接口也被視為內部接口慢洋。

導入的名稱應始終被視為實現(xiàn)細節(jié)塘雳。其他模塊不得依賴對此類導入名稱的間接訪問,除非它們是包含模塊的API中明確記錄的一部分普筹,例如os.path__init__暴露了子模塊功能的軟件包模塊败明。

編程建議

  • 應該以不損害Python其他實現(xiàn)(PyPy,Jython太防,IronPython妻顶,Cython,Psyco等)的方式編寫代碼蜒车。

  • 與單例(如None)的比較應始終使用is或進行is not讳嘱,絕不能使用相等運算符進行。

    另外酿愧,當心if x您的意思if x is not None呢燥,例如當測試是否將默認設置為None的變量或參數(shù)設置為其他值時,請當心編寫寓娩。另一個值可能具有在布爾上下文中可能為false的類型(例如容器)叛氨!

  • 使用is not運算符而不是not ... is呼渣。盡管兩個表達式在功能上相同,但前者更易讀和首選寞埠。

    Yes:

    if foo is not None:
    

    No:

    if not foo is None:
    
  • 當有比較豐富排序執(zhí)行的操作屁置,最好是實現(xiàn)所有六個操作(__eq____ne__仁连,__lt__蓝角,__le____gt__饭冬,__ge__)而不是依靠其他代碼使鹅,只行使特定的比較。

    為了最大程度地減少工作量昌抠,functools.total_ordering()裝飾器提供了一種生成缺少的比較方法的工具患朱。

  • 始終使用def語句而不是將lambda表達式直接綁定到標識符的賦值語句。

    Yes:

    def f(x): return 2*x
    

    No:

    f = lambda x: 2*x
    

    第一種形式表示結果函數(shù)對象的名稱專門為“ f”炊苫,而不是通用的“ <lambda>”裁厅。通常,這對于回溯和字符串表示形式更為有用侨艾。使用賦值語句消除了lambda表達式相對于顯式def語句(即执虹,可以將其嵌入較大的表達式中)提供的唯一好處。

  • Exception而不是從BaseException派生異常

    類命名約定在此處適用唠梨,但是如果異常是錯誤袋励,則應在異常類中添加后綴“ Error”。

  • 在Python 2中引發(fā)異常時当叭,請使用茬故,raise ValueError('message')而不要使用舊的形式 raise ValueError, 'message'

    后一種形式不是合法的Python 3語法科展。

    使用括號的形式還意味著均牢,當異常參數(shù)很長或包含字符串格式時,由于包含括號才睹,因此不需要使用行繼續(xù)符徘跪。

  • 捕獲異常時,請盡可能提及特定的異常琅攘,而不要使用空except:子句垮庐。

    例如,使用:

    try:
        import platform_specific_module
    except ImportError:
        platform_specific_module = None
    

    except:子句將捕獲SystemExit和KeyboardInterrupt異常坞琴,這使得使用Control-C中斷程序更加困難哨查,并且可以掩蓋其他問題。如果要捕獲所有表明程序錯誤的異常剧辐,請使用except Exception:

    一個好的經(jīng)驗法則是將空的'except'子句的使用限制為兩種情況:

    1. 如果異常處理程序將打印輸出或記錄回溯寒亥;至少用戶會意識到發(fā)生了錯誤邮府。
    2. 如果代碼需要做一些清理工作,但是讓異常向上傳播raise溉奕。try...finally可能是處理這種情況的更好方法褂傀。
  • 將捕獲的異常綁定到名稱時,最好使用Python 2.6中添加的顯式名稱綁定語法:

    try:
        process_data()
    except Exception as exc:
        raise DataProcessingFailedError(str(exc))
    

    這是Python 3中唯一支持的語法加勤,并且避免了與較早的基于逗號的語法相關的歧義問題仙辟。

  • 捕獲操作系統(tǒng)錯誤時,與errno值的自省相比鳄梅,更喜歡Python 3.3中引入的顯式異常層次結構叠国。

  • 此外,對于所有try / except子句戴尸,請將try子句限制為所需的絕對最小數(shù)量的代碼粟焊。同樣,這避免了掩蓋錯誤校赤。

    Yes:

    try:
        value = collection[key]
    except KeyError:
        return key_not_found(key)
    else:
        return handle_value(value)
    

    No:

    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語句也是可以接受的。

  • 每當他們執(zhí)行除獲取和釋放資源以外的其他操作時怜奖,都應通過單獨的函數(shù)或方法來調用上下文管理器浑测。例如:

    Yes:

    with conn.begin_transaction():
        do_stuff_in_transaction(conn)
    

    No:

    with conn:
        do_stuff_in_transaction(conn)
    

    后面的示例沒有提供任何信息來指示__enter____exit__方法在事務處理后關閉連接以外的操作。在這種情況下歪玲,暴露很重要迁央。

  • 在返回語句中保持一致。函數(shù)中的所有return語句應該返回一個表達式滥崩,或者都不返回岖圈。如果任何return語句返回一個表達式,則不返回任何值的任何return語句return None都應將其顯式聲明為钙皮,并且在函數(shù)末尾(如果可以到達)應存在一個顯式return語句蜂科。

    Yes:

    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)
    

    No:

    def foo(x):
        if x >= 0:
            return math.sqrt(x)
    
    def bar(x):
        if x < 0:
            return
        return math.sqrt(x)
    
  • 使用字符串方法而不是字符串模塊。

    字符串方法總是更快短条,并且與unicode字符串共享相同的API

  • 使用''.startswith()''.endswith()代替字符串切片來檢查前綴或后綴导匣。

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

    在檢查對象是否為字符串時贡定,請記住它也可能是unicode字符串!在Python 2中可都,str和unicode具有一個公共基類basestring缓待,因此您可以執(zhí)行以下操作:

    if isinstance(obj, basestring):
    

    請注意蚓耽,在Python 3中,unicodebasestring不再存在(只有str)旋炒,并且bytes對象不再是字符串的一種(而是整數(shù)序列)

  • 對于序列(字符串田晚,列表,元組)国葬,請使用空序列為假的事實:

    Yes:

    if not seq:
    if seq:
    

    No:

    if len(seq):
    if not len(seq):
    
  • 不要寫依賴大量尾隨空格的字符串文字贤徒。這種尾隨的空格在視覺上是無法區(qū)分的,某些編輯器(或更近期的reindent.py)將對其進行修剪汇四。

  • 不要使用==以下方法將布爾值與True或False進行比較:

    Yes:

    if greeting:
    

    No:

    if greeting == True:
    

    Worse:

    if greeting is True:
    

參考

PEP8

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末接奈,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子通孽,更是在濱河造成了極大的恐慌序宦,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件背苦,死亡現(xiàn)場離奇詭異互捌,居然都是意外死亡,警方通過查閱死者的電腦和手機行剂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門秕噪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人厚宰,你說我怎么就攤上這事腌巾。” “怎么了铲觉?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵澈蝙,是天一觀的道長。 經(jīng)常有香客問我撵幽,道長灯荧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任盐杂,我火速辦了婚禮逗载,結果婚禮上,老公的妹妹穿的比我還像新娘况褪。我一直安慰自己撕贞,他們只是感情好,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布测垛。 她就那樣靜靜地躺著捏膨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上号涯,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天目胡,我揣著相機與錄音,去河邊找鬼链快。 笑死誉己,一個胖子當著我的面吹牛,可吹牛的內容都是我干的域蜗。 我是一名探鬼主播巨双,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼霉祸!你這毒婦竟也來了筑累?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤丝蹭,失蹤者是張志新(化名)和其女友劉穎慢宗,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體奔穿,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡镜沽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了贱田。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缅茉。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖湘换,靈堂內的尸體忽然破棺而出宾舅,到底是詐尸還是另有隱情统阿,我是刑警寧澤彩倚,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站扶平,受9級特大地震影響帆离,放射性物質發(fā)生泄漏。R本人自食惡果不足惜结澄,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一哥谷、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧麻献,春花似錦们妥、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春惑惶,著一層夾襖步出監(jiān)牢的瞬間煮盼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工带污, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留僵控,地道東北人。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓鱼冀,卻偏偏與公主長得像报破,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子千绪,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345