Python風格指南

PEP 8風格指南

PEP是Python Enhancement Proposal的縮寫,一般翻譯爲「Python加強提案」。每一個PEP都是一份爲Python社區提供的指導Python往更好的方向發展的技術文檔,其中的第8號加強提案(PEP 8)是針對Python語言編訂的代碼風格指南。儘管咱們能夠在保證語法沒有問題的前提下隨意書寫Python代碼,可是在實際開發中,採用一致的風格書寫出可讀性強的代碼是每一個專業的程序員應該作到的事情,也是每一個公司的編程規範中會提出的要求,這些在多人協做開發一個項目(團隊開發)的時候顯得尤其重要。咱們能夠從Python官方網站的PEP 8連接中找到該文檔,下面咱們對該文檔的關鍵部分作一個簡單的總結。html

分號

不要在行尾加分號, 也不要用分號將兩條命令放在同一行。python

行長度

每行不超過80個字符。git

例外:程序員

  • 長的導入模塊語句
  • 註釋裏的URL
  • 不要使用反斜槓鏈接行

Python會將圓括號,中括號花括號中的行隱式的鏈接起來 , 你能夠利用這個特色. 若是須要, 你能夠在表達式外圍增長一對額外的圓括號。github

Yes: foo_bar(self, width, height, color='black', design=None, x='foo',
             emphasis=None, highlight=0)

     if (width == 0 and height == 0 and
         color == 'red' and emphasis == 'strong'):

若是一個文本字符串在一行放不下, 可使用圓括號來實現隱式行鏈接:算法

x = ('This will build a very long long '
     'long long long long long long string')

在註釋中,若是必要,將長的URL放在一行上。編程

Yes:  # See details at
      # http://www.example.com/us/developer/documentation/api/content/v2.0/csv_file_name_extension_full_specification.html
No:  # See details at
     # http://www.example.com/us/developer/documentation/api/content/\
     # v2.0/csv_file_name_extension_full_specification.html

括號

寧缺毋濫的使用括號。
除非是用於實現行鏈接, 不然不要在返回語句或條件語句中使用括號。 不過在元組兩邊使用括號是能夠的。api

Yes: if foo:
         bar()
     while x:
         x = bar()
     if x and y:
         bar()
     if not x:
         bar()
     return foo
     for (x, y) in dict.items(): ...
No:  if (x):
         bar()
     if not(x):
         bar()
     return (foo)

空格的使用

  • 使用空格來表示縮進而不要用製表符(Tab)。這一點對習慣了其餘編程語言的人來講簡直以爲不可理喻,由於絕大多數的程序員都會用Tab來表示縮進,可是要知道Python並無像C/C++或Java那樣的用花括號來構造一個代碼塊的語法,在Python中分支和循環結構都使用縮進來表示哪些代碼屬於同一個級別,鑑於此Python代碼對縮進以及縮進寬度的依賴比其餘不少語言都強得多。在不一樣的編輯器中,Tab的寬度多是二、4或8個字符,甚至是其餘更離譜的值,用Tab來表示縮進對Python代碼來講多是一場災難。
  • 和語法相關的每一層縮進都用4個空格來表示。
  • 除了首行以外的其他各行都應該在正常的縮進寬度上再加上4個空格。
  • 函數和類的定義,代碼先後都要用兩個空行進行分隔。
  • 在同一個類中,各個方法之間應該用一個空行進行分隔。
  • 二元運算符的左右兩側應該保留一個空格,並且只要一個空格就好。

標識符命名

PEP 8倡導用不一樣的命名風格來命名Python中不一樣的標識符,以便在閱讀代碼時可以經過標識符的名稱來肯定該標識符在Python中扮演了怎樣的角色(在這一點上,Python本身的內置模塊以及某些第三方模塊都作得並非很好)。app

  • 變量、函數和屬性應該使用小寫字母來拼寫,若是有多個單詞就使用下劃線進行鏈接。
  • 類中受保護的實例屬性,應該以一個下劃線開頭。
  • 類中私有的實例屬性,應該以兩個下劃線開頭。
  • 類和異常的命名,應該每一個單詞首字母大寫。
  • 模塊級別的常量,應該採用全大寫字母,若是有多個單詞就用下劃線進行鏈接。
  • 類的實例方法,應該把第一個參數命名爲self以表示對象自身。
  • 類的類方法,應該把第一個參數命名爲cls以表示該類自身。

表達式和語句

在Python之禪(可使用import this查看)中有這麼一句名言:「There should be one-- and preferably only one --obvious way to do it.」,翻譯成中文是「作一件事應該有並且最好只有一種確切的作法」,這句話傳達的思想在PEP 8中也是無處不在的。編程語言

  • 採用內聯形式的否認詞,而不要把否認詞放在整個表達式的前面。例如if a is not b就比if not a is b更容易讓人理解。
  • 不要用檢查長度的方式來判斷字符串、列表等是否爲None或者沒有元素,應該用if not x這樣的寫法來檢查它。
  • 就算if分支、for循環、except異常捕獲等中只有一行代碼,也不要將代碼和if、for、except等寫在一塊兒,分開寫纔會讓代碼更清晰。
  • import語句老是放在文件開頭的地方。
  • 引入模塊的時候,from math import sqrt比import math更好。
  • 若是有多個import語句,應該將其分爲三部分,從上到下分別是Python標準模塊、第三方模塊和自定義模塊,每一個部份內部應該按照模塊名稱的字母表順序來排列。

註釋

確保對模塊, 函數, 方法和行內註釋使用正確的風格。

塊註釋和行註釋

最須要寫註釋的是代碼中那些技巧性的部分。若是你在下次代碼審查的時候必須解釋一下,那麼你應該如今就給它寫註釋。對於複雜的操做, 應該在其操做開始前寫上若干行註釋。對於不是一目瞭然的代碼, 應在其行尾添加註釋。

# We use a weighted dictionary search to find out where i is in
# the array.  We extrapolate position based on the largest num
# in the array and the array size and then do binary search to
# get the exact number.

if i & (i-1) == 0:        # True if i is 0 or a power of 2.

爲了提升可讀性,註釋應該至少離開代碼2個空格。
另外一方面, 毫不要描述代碼。 假設閱讀代碼的人比你更懂Python, 他只是不知道你的代碼要作什麼。

文檔字符串

Python有一種獨一無二的的註釋方式: 使用文檔字符串。

文檔字符串是包, 模塊,類或函數裏的第一個語句。
這些字符串能夠經過對象的__doc__成員被自動提取, 而且被pydoc所用。(你能夠在你的模塊上運行pydoc試一把, 看看它長什麼樣)。
咱們對文檔字符串的慣例是使用三重雙引號"""( PEP-257 )。

一個文檔字符串應該這樣組織:

  • 首先是一行以句號,問號或驚歎號結尾的概述(或者該文檔字符串單純只有一行)。
  • 接着是一個空行。
  • 接着是文檔字符串剩下的部分,它應該與文檔字符串的第一行的第一個引號對齊。
函數文檔字符串

一個函數必需要有文檔字符串,除非它知足如下條件:

  • 外部不可見
  • 很是短小
  • 簡單明瞭

文檔字符串應該包含函數作什麼以及輸入和輸出的詳細描述。一般不該該描述「怎麼作」,除非是一些複雜的算法。

文檔字符串應該提供足夠的信息,當別人編寫代碼調用該函數的時候,它不須要看一行代碼,只要看文檔字符串就夠了。對於複雜的代碼,在代碼旁邊加註釋會比文檔字符串更有意義。
函數的文檔字符串格式,將函數按照參數、返回值、拋出異常等信息分小節進行描述,每小節應該以一個標題行開始,標題行以冒號結尾,除標題行外, 節的其餘內容應被縮進2個空格。

Args:
    列出每一個參數的名字, 並在名字後使用一個冒號和一個空格, 分隔對該參數的描述.若是描述太長超過了單行80字符,使用2或者4個空格的懸掛縮進(與文件其餘部分保持一致). 描述應該包括所需的類型和含義. 若是一個函數接受*foo(可變長度參數列表)或者**bar (任意關鍵字參數), 應該詳細列出*foo和**bar.
Returns: (或者 Yields: 用於生成器)
    描述返回值的類型和語義. 若是函數返回None, 這一部分能夠省略.
Raises:
    列出與接口有關的全部異常.
def fetch_bigtable_rows(big_table, keys, other_silly_variable=None):
    """Fetches rows from a Bigtable.

    Retrieves rows pertaining to the given keys from the Table instance
    represented by big_table.  Silly things may happen if
    other_silly_variable is not None.

    Args:
        big_table: An open Bigtable Table instance.
        keys: A sequence of strings representing the key of each table row
            to fetch.
        other_silly_variable: Another optional variable, that has a much
            longer name than the other args, and which does nothing.

    Returns:
        A dict mapping keys to the corresponding table row data
        fetched. Each row is represented as a tuple of strings. For
        example:

        {'Serak': ('Rigel VII', 'Preparer'),
         'Zim': ('Irk', 'Invader'),
         'Lrrr': ('Omicron Persei 8', 'Emperor')}

        If a key from the keys argument is missing from the dictionary,
        then that row was not found in the table.

    Raises:
        IOError: An error occurred accessing the bigtable.Table object.
    """
    pass
類文檔字符串

類應該在其定義下有一個用於描述該類的文檔字符串。 若是你的類有公共屬性(Attributes), 那麼文檔中應該有一個屬性(Attributes)段。 而且應該遵照和函數參數相同的格式。

class SampleClass(object):
    """Summary of class here.

    Longer class information....
    Longer class information....

    Attributes:
        likes_spam: A boolean indicating if we like SPAM or not.
        eggs: An integer count of the eggs we have laid.
    """

    def __init__(self, likes_spam=False):
        """Inits SampleClass with blah."""
        self.likes_spam = likes_spam
        self.eggs = 0

    def public_method(self):
        """Performs operation blah."""

慣例

「慣例」這個詞指的是「習慣的作法,常規的辦法,一向的作法」,與這個詞對應的英文單詞叫「idiom」。因爲Python跟其餘不少編程語言在語法和使用上仍是有比較顯著的差異,所以做爲一個Python開發者若是不能掌握這些慣例,就沒法寫出「Pythonic」的代碼。下面咱們總結了一些在Python開發中的慣用的代碼。

讓代碼既能夠被導入又能夠被執行。

if __name__ == '__main__':

用下面的方式判斷邏輯「真」或「假」。

if x:
if not x:

好的代碼:

name = 'Safe'
pets = ['Dog', 'Cat', 'Hamster']
owners = {'Safe': 'Cat', 'George': 'Dog'}
if name and pets and owners:
    print('We have pets!')

很差的代碼:

if name != '' and len(pets) > 0 and owners != {}:
    print('We have pets!')

善於使用in運算符。

if x in items: # 包含
for x in items: # 迭代

好的代碼:

name = 'Safe Hammad'
if 'H' in name:
    print('This name has an H in it!')

很差的代碼:

name = 'Safe Hammad'
if name.find('H') != -1:
    print('This name has an H in it!')

不使用臨時變量交換兩個值。

a, b = b, a

用序列構建字符串。

好的代碼

chars = ['S', 'a', 'f', 'e']
name = ''.join(chars)
print(name) # Safe

很差的代碼

chars = ['S', 'a', 'f', 'e']
name = ''
for char in chars:
    name += char
print(name) # Safe

EAFP 優於 LBYL

「It's Easier to Ask for Forgiveness than Permission.」
「Look Before You Leap」

好的代碼

d = {'x': '5'}
try:
    value = int(d['x'])
except (KeyError, TypeError, ValueError):
    value = None

很差的代碼

d = {'x': '5'}
if 'x' in d and \
    isinstance(d['x'], str) and \
    d['x'].isdigit():
    value = int(d['x'])
else:
    value = None

使用enumerate進行迭代。

好的代碼

fruits = ['orange', 'grape', 'pitaya', 'blueberry']
for index, fruit in enumerate(fruits):
    print(index, ':', fruit)

很差的代碼

fruits = ['orange', 'grape', 'pitaya', 'blueberry']
index = 0
for fruit in fruits:
    print(index, ':', fruit)
    index += 1

用生成式生成列表。

好的代碼

data = [7, 20, 3, 15, 11]
result = [num * 3 for num in data if num > 10]
print(result)  # [60, 45, 33]

很差的代碼

data = [7, 20, 3, 15, 11]
result = []
for i in data:
    if i > 10:
        result.append(i * 3)
print(result)  # [60, 45, 33]

用zip組合鍵和值來建立字典。

好的代碼

keys = ['Safe', 'Bob', 'Thomas']
values = ['Hammad', 'Builder', 'Engine']
d = dict(zip(keys, values))
print(d) # {'Bob': 'Builder',
         #  'Safe': 'Hammad',
         #  'Thomas': 'Engine'}

很差的代碼

keys = ['Safe', 'Bob', 'Thomas']
values = ['Hammad', 'Builder', 'Engine']
d = {}
for i, key in enumerate(keys):
    d[keys] = values[i]
print(d) # {'Bob': 'Builder',
         #  'Safe': 'Hammad',
         #  'Thomas': 'Engine'}

參考

PEP8風格指南

谷歌開源項目風格規範

慣例

相關文章
相關標籤/搜索