Python編碼規范

Python編碼規范

1 排版

1.1 Indentation縮進

在參數過多時適當縮進

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)

換行應該使用同級的縮進

Yes:

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)

Yes:

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

以關系符開始應該增加縮進

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

1.2 最大行長

所有行最大長度被限制在79個字符

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

過長需要續行使用’\’二元操作符的位置

No: 操作符里操作數過遠

income = (gross_wages +
          taxable_interest +
         (dividends - qualified_dividends) -
          ira_deduction -
          student_loan_interest)

Yes: 正確的方式可以更容易的去匹配操作符與操作數

income = (gross_wages
        + taxable_interest
        + (dividends - qualified_dividends)
        - ira_deduction
        - student_loan_interest)

1.3空行

  • 頂層函數和類定義:前后兩行空白
  • 類方法定義:前后一行空白
  • 應該添加額外空行來曲分不同組的函數
  • 在函數定義內部使用空行來分隔不同的邏輯片段
  • 源文件編碼:

Python2使用ASCII編碼或者Python3使用UTF-8編碼,不應該聲明編碼類型。
所以在使用Python2時,我們應該在源文件開始聲明編碼類型。
#coding=utf-8

1.4 Import引入

  • 引入模塊應該分行

Yes:

import os       
import sys

No:

import sys, os

It's okay to say this though:

from subprocess import Popen, PIPE

引入聲明應該總是在文件頂部,僅在任何模塊的注釋和文檔字符后面,同時也要處在全局變量和常量聲明之前。

引入應該按照下面的順序分組并書寫

  1. 標準庫
  2. 相關第三方庫
  3. 本地 應用/庫 特殊量的引入

你應該用空行隔開每個分組

? 在用戶包/或者可引用的文件夾外的文檔內import 模塊應該使用絕對引用的方式

import mypkg.sibling
from mypkg import sibling
from mypkg.sibling import example

? 在用戶包內import 模塊應該使用相對引用的方式,確保包內路徑的可靠性

from . import sibling

從當前目錄下引用

from .. import x

從當前目錄上層文件夾引用

from .sibling import example

從當前目錄下sibling層引用

標準庫應當總是絕對引用

應當盡量避免使用下面的引入方式:

from <module> import *

1.4 行內注釋

  • 應該保守的使用行內注釋
  • 行內注釋應該至少與代碼間隔兩個空格
  • 應避免無意義的注釋

如下

x = x + 1                 # Increment x

這樣會更有意義:

x = x + 1                 # Compensate for border

2. 命名要求

2.1 最重要的規則

作為為其他用戶開放的公共變量的名字必須遵循管理,而且能夠顯示它的用途

2.2 命名形式

有很多不同的命名方式,下面的命名方式都是可以使用

  • b (single lowercase letter)
  • B (single uppercase letter)
  • lowercase
  • lower_case_with_underscores
  • UPPERCASE
  • UPPER_CASE_WITH_UNDERSCORES
  • CapitalizedWords (or CapWords, or CamelCase -- so named because of the bumpy look of its letters ). This is also sometimes known as StudlyCaps.

Note: 當使用駝峰是命名的時候,應當將命名中的縮寫全部用大寫表示. 因此 HTTPServerError 要比HttpServerError更好。
mixedCase (不同于CapitalizedWords在首字母的大小寫上),

Capitalized_Words_With_Underscores (這是個很丑的方式)

  • 也可以使用一個獨特簡短的前綴為一組相關的變量進行命這在Python里使用的并不多。
  • 還需要補充說明的一些規則

_single_leading_underscore : 弱的內部變量標示. 當使用語句
from M import *語句引入模塊時并不會引用以單個下劃線開頭的變量。

single_trailing_underscore_ : 使用后單個下劃線來避免變量名與python關鍵詞的沖突,如:class_class

__double_leading_underscore : 當命名一個類屬性是,以雙下劃線開頭, 啟用命名矯正功能(在類FooBar內, __boo變成了_FooBar__boo)。

double_leading_and_trailing_underscore : 以雙下劃綫開頭和結尾Python中可以重載的一些魔法方法。

E.g.__init__ , __import__ or __file__ . 不要去創造相似的名字。

3. 指定的: 命名規則

3.1 需要避免的命名

  • 永遠不要使用字母'l' (小寫的字母L), 'O' (大寫字母的o), 或者 'I' (大寫字母的i) 作為單個字母的變量名。

3.2 包和模塊名

  • 模塊應該用短的,而且全是小寫字母的名字。

  • 使用下劃線分割,當使用下劃線可以提高模塊命名的可讀性時。

  • Python包應該也使用短的,而且全小寫的名字,并不推薦使用下劃線分割。

  • 當存在一個使用C或C++編寫的模塊,同時包含一個Python模塊為其提供高層封裝時, C/C++ 模塊應該用前下劃線開始(e.g. _socket )。

3.3 類命名

  • 類名通常應該使用首字母大寫的命名規則(CapWords convention)。

  • 函數命名規則可以被使用當接口被記錄在文檔中,同時主要用于隨時可以調用的函數
    The naming convention for functions may be used instead in cases where the interface is documented and used primarily as a callable.

  • 需要注意的是,對于內建函數的命名,這里與一個不同的規則,他們一般是單獨的一個單詞,又是是兩個單詞。

  • CapWords convention,僅僅用于異常名和內建的常量

Note that there is a separate convention for builtin names: most builtin names are single words (or two words run together), with the CapWords convention used only for exception names and builtin constants.

3.4 類型變量名

  • 類型變量名通常應該使用CapWords,且更傾向于使用簡短的名字,如: 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)
    

3.5 異常名

因為異常是一個類, 按照已經提供的類命名方法來命名。你還需要在你的異常名后面添加"Error"后綴(如果它真的是一個錯誤).

3.6 全局變量命名

  • (假設這些變量僅僅在一個模塊內部使用) 它們的命名規則和函數是一樣的。

  • 被設計成為使用from M import *語句來引入的模塊,應該使用__all__機制 來防止引入全局量, 或者使用一個下劃線為前綴的舊命名規則為全局量命名(這樣就指明了這些全局量是非公開的)。

3.7 函數命名

函數名應該是小寫,有必要為了提高可閱讀性可以使用下劃線將單詞分開。

mixedCase被允許使用,僅僅是為了延續上文中使用的經典命名方式(e.g. threading.py)來維持向前的兼容性

3.8 函數和方法參數

  • 總是使用self作為實例方法的第一個參數。

  • 總是使用cls作為類方法的第一個參數。

  • 如果一個函數的參數的名字與保留字沖突,為其添加一個后下劃線要比縮寫或者污染這個變量名更好。因此class_要比clss好。 (可能更好的方法是使用同義詞來避免這樣的沖突。)

3.9 方法名和實例變量

  • 使用函數命名規則: 有必要小寫那些使用下劃線分隔的單詞,如果能夠提高可讀性。

  • 使用單個前下劃線來為非公開的方法和實例變量命名。

  • 為了避免和子類的命名沖突,使用兩個前下劃線來啟用Python的命名矯正功能。

    Python用類型名來矯正這些名字。如果類型Foo有一個屬性,命名為__a,使用Foo.__a并不能調用它。(堅持的用戶仍然可以使用Foo._Foo__a來調用。) 通常雙下劃線應該是用于被設計成可繼承的類,來避免類屬性與子類屬性的命名沖突。

注意: 對于__names的使用時存在一些爭議的(見下文)。

3.10 常量

  • 常量一般定義于模塊層級,使用用下劃線分隔的全大寫字母來命名。

    Examples include MAX_OVERFLOW and TOTAL .

3.11 繼承的設計

  • 總是決定一個方法或者變量(均被稱為屬性)應該是公開還是不公開的。如果存在疑問就選擇非公開。那么在未來將其變成公開的會比變成非公開的更簡單。

  • 公開的屬性是那些你希望開放給不相關的客戶端來使用的屬性,你的恪守承諾可以讓這些不相關的使用者,消除向下不兼容的變化。非公開的屬性是指,不會被第三方調用的屬性。你不為非公開的屬性提供不變化或者不被移除的保證。

    我們不使用“private”這里,是因為在Python里沒有真正私有的變量(在不付出大量非必要的工作量下)。

  • 另一種范疇的屬性是那些“subclass API”(通常被聲明為“protected”在其他語言中)。有些類是設計成為被繼承的,不是去擴展就是修改這些類的行為。
    當設計這樣的類時,要仔細的去做出明確的決定,哪些屬性是公開的,哪些屬性是子類接口,哪些屬性是真的只被你的基類使用。

有了這些考慮,下面是一些python化的指引:

  • 公開的屬性應該沒有前下劃線。

  • 如果你的公開屬性名與保留字沖突,在其名字尾部添加單下劃線。(但是, 盡管有這條規則,對于已知是一個類的任何變量或者參數,特別是類方法的第一個參數,cls都是一個更好的拼寫)

  • 對于簡單的公開的數據屬性,最好僅僅暴露屬性名,而不要存取器方法。記住Python對未來的增強提供簡單的方式。如果你發現一個簡單的數據需要去擴展函數的行為,在這種情況下,使用內置的“property”函數來隱藏簡單數據在讀寫或者刪除時,產生的其他行為。

Note 1: Property只工作于新式的類。

Note 2: Try to keep the functional behavior side-effect free, although side-effects such as caching are generally fine.

Note 3: 在計算密集型的操作中避免使用property的方法。

  • 如果你的類是被設計成為被繼承的,同時,你有那些你不希望子類去使用的屬性,那么考慮使用前雙下劃線(不帶后下劃線)去命名它們。這觸發了Python的命名矯正算法,這時,類名會被加入屬性名中。這對避免屬性名沖突,當子類并不是故意的包含哪些相同的名字。

Note 1: 注意只有簡單的類命名被用于矯正命名。所以當有一個子類選擇相同的類名和相同的屬性名,你還是會有命名沖突。

Note 2: 命名矯正較不方便的用在確定的用途上,比如調試或者獲得屬性(getattr()方法),盡管命名矯正算法已經有了很好的文檔,也可以簡單的手動實施。

Note 3: 并不是每個人都喜歡命名矯正,試著去平衡的使用它來降低高級調用者潛在的使用中出現意外的命名沖突的可能性。

4. 公開和內部接口

  • 只是對公開接口,保證向前的兼容性,所以使用者能夠清楚的判斷公開接口和內部接口是很重要的。
  • 寫入文檔的接口被認為是公開的,除非文檔明確說明,這些接口是臨時得或者內部的,從而不在向前兼容的保證中。所有沒有被寫入文檔的接口應該是內部的。
  • 為了更好的支持內省,模塊應該明確的聲明那些公開的API的名字,使用模塊的__all__屬性。設置__all__屬性為一個空的列表,說明這個模塊沒有任何公開的API。
  • __all__的適當設定一樣,內部接口(包、模塊、類、函數、屬性或者其他名字)應該仍然使用前單下劃線作為前綴。
  • 一個接口被同樣認為是內部的,當任何包含的命名空間(包、模塊或者類)被認為是內部的。

5. 編程建議

5.1 基本建議

  • 代碼應該以不會不利于其他實現的Python(PyPy、Jython、IronPython、Cython,、Psyco等)的方式。

    例如,不要依賴CPython對字符串有效率的連接方法,如a+=b或者a=a+b的表述。這個優化在CPython(只對幾種類型生效)中是脆弱的,同時,這個方式不會被實現,當解釋器不使用refcounting。對于庫中性能敏感的部分,應該是''.join()形式。這保證在不同的Python實現中,連接字符串操作是線性時間的。

5.2 比較操作

  • 與單個變量的比較,比如None,應該總是用is或者is not來操作,永遠不應該是等號操作符。

  • 同樣的,小心書寫if x當你的真實意圖是if x is not None

    例如,當測試一個變量或者參數默認是None,是否被設置成為其他值是,這個值,可能是一個類型(例如容器),可以在布爾層面是False。a=[],bool(a)False

  • 使用is not操作符而不是not…is。雖然兩個表述都是正確的,但是前者更易讀也更推薦

    Yes:

      if foo is not None:
    

    No:

      if not foo is None:
    
  • 當用rich comparisons實現排序操作,最好實現全部留個比較操作符(eq , ne ,lt , le , gt , ge)而不是依賴其他代碼只演習了一個特定的比較
    為了最小化我們的工作量,functools.total_ordering()裝飾器提供了一個生成缺失比較方法的工具。
    PEP207指出自反性規則是被Python假設存在的。因此解釋器可能會用x<y交換y>x,用y>=x交換x<=y,也可能交換x==y和x!=y的參數。sort()和min()操作保證使用<操作符,max()函數使用>操作符。盡管如此,最好還是實施全部留個操作符,那么混淆將不會在其他地方產生。

  • 總是使用def聲明,而不是一個賦值聲明,將一個變量直接綁定到一個lambda表達式。

    Yes:

      def f(x): return 2*x
    

    No:

      f = lambda x: 2*x
    

    第一種形式意味著結果函數對象的名字是f而不是一般的’<lambda>’,這在棧追蹤中會更有用,同時顯示它的名字。賦值聲明的使用消除唯一一個lambda表達式的好處,就是可以一個提供越過def聲明(它可以被嵌入在一個大的表達式中)。

5.3 異常處理

  • 從Exception派生異常,而不是BaseException。直接從BaseException繼承,是留給那些總是被捕捉為一個錯誤的東西的異常。

    設計基于代碼捕捉異常的特性的異常分層是需要的,而不是找到異常拋出的地方。旨在以編程方式回答“什么錯了”的問題,而不是只是聲明”出現了一個錯誤”。類命名規則被使用在這里,盡管你需要為你的異常類添加Error后綴,如果這個異常是一個錯誤。沒有錯誤的異常是被用在非本地的流控制或者其他形式的不需要特殊后綴的信號。

  • 適當的使用異常鏈。在Python3中,raise X from Y應該被用于明確的指出替換,而不丟失原始的棧追蹤信息。

    當故意的替換一個內部異常(在Python2中使用raise X,或者在Python3.3+中使用raise X from None),需要保證相關的細節被轉移到了新的異常中。(如,保留屬性名,當轉化KeyError為AttributeError,或者在新異常中嵌入原始異常的說明)

  • 當在Python2中觸發一個異常時,使用raise ValueError('message')來替代老式的用法raise ValueError,'message'
    后者在Python3中不合法。

  • 在捕獲異常時,盡可能的提出特定的異常,而不是裸的except: clause.因為很有可能會捕捉到別的異常,如鍵盤中斷異常。

  • 當綁定一個捕捉到的異常到一個名字,盡量使用在Python2.6中增加的明確的命名綁定語法

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

    這是唯一在Python3中支持的語法,同時消除了老式逗號語法的混淆。

  • 當捕獲操作系統錯誤時,最好是使用Python3.3中引入的明確的異常層次,而不是errno的值。

  • 額外的說明,對于全部的try/except語句,限制try中的語句的數量到一個盡量少的量,這可以避免掩蓋bugs

    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)
    

5.4 with語句

  • 當一個資源對于一個特定部分的代碼是一個內部變量時,使用with聲明去保證,它會被使用后迅速、可靠的清理。一個try/finally聲明也是可以接受的。

  • 上下文管理器應該被通過不同的函數或者方法調用,無論它們做除了獲取和釋放資源的任務,例如:

    Yes:

      with conn.begin_transaction():
          do_stuff_in_transaction(conn)
    

    No:

      with conn:
          do_stuff_in_transaction(conn)
    

    后一個例子并沒有提供任何信息去指明,enterexit方法在做什么除了在傳輸后關閉連接,在這種情況下,明確是很重要的。

5.5 return語句

  • 對return聲明一致。一個函數中全部return語句要么都返回一個表達式,要么任何一個都不。如果任何一個返回語句返回一個表達式,任何那些沒有返回值的返回語句,應該顯示的返回None,一個顯示的返回聲明,應該在函數的結尾保留(如果可以到達)。

    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)
    

5.6 字符串操作

  • 使用字符串方法而不是string模塊
    字符串方法一般要快的多,同時和unicode字符串使用相同的API。如果需要想前兼容比Python2.0更老的版本,需要重寫這些規則。

  • 使用.startswith().endswith()而不是字符串切分,來檢查前綴或者后綴。
    .startswith().endswith() 更干凈的,而且更不容易出錯的。

    For example:

    Yes: if foo.startswith('bar'):

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

5.7 類型比較

  • 對象類型比較應該總是使用isinstance() 而不是直接比較它們們的類型。

    Yes: if isinstance(obj, int):

    No: if type(obj) is type(1):

  • 當檢查一個對象是不是一個字符串。請留意,他可能是也是一個unicode字符串,在Python2中,str和unicode有相同的基類,basestring,所以你可以這樣做:

    if isinstance(obj, basestring):

注意在Python3中unicode和basestring都不存在了(只有str類型),同時一個字節對象也不再是一種類型的字符串了(它是一個整型序列了)。

  • 對于序列,(字符串,列表,元組),使用空序列就是False的事實。

    Yes:

      if not seq:
      if seq:
    

    No:
    if len(seq):
    if not len(seq):

  • 不要寫字符串字面值依賴于顯著的空格結尾,這些尾部的空格是視覺上不能區別的,同時有些編輯器(更近點的, reindent.py)會修剪它們

  • 不要比較布爾值與True或者False使用==

    Yes: if greeting:

    No: if greeting == True:

    Worse: if greeting is True:

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,882評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,208評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,746評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,666評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,477評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,960評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,047評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,200評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,726評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,617評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,807評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,327評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,049評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,425評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,674評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,432評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,769評論 2 372

推薦閱讀更多精彩內容

  • --< > 令人討厭的小人物身上有著愚蠢的一致性 --(A Foolish Consistency is the ...
    LittleWizard閱讀 3,254評論 0 4
  • PEP介紹 PEP是 Python Enhancement Proposal 的縮寫,是Python增強建議書的意...
    JasonDing閱讀 4,840評論 3 40
  • 決定開始Python之路了,利用業余時間,爭取更深入學習Python。編程語言不是藝術,而是工作或者說是工具,所以...
    鴻佛閱讀 655評論 0 3
  • 代碼編排 縮進。4個空格的縮進(編輯器都可以完成此功能),不使用Tab,更不能混合使用Tab和空格。 每行最大長度...
    高陽劉閱讀 655評論 0 0
  • 代碼編排 縮進。4個空格的縮進(編輯器都可以完成此功能),不使用Tap,更不能混合使用Tap和空格。 每行最大長度...
    Spareribs閱讀 27,434評論 2 26