Python風格指南


blog:https://www.cnblogs.com/Rohn/

PEP 8風格指南

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

分號

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

行長度

每行不超過80個字符。

例外:

  • 長的導入模塊語句
  • 注釋里的URL
  • 不要使用反斜杠連接行

Python會將圓括號,中括號花括號中的行隱式的連接起來 , 你可以利用這個特點. 如果需要, 你可以在表達式外圍增加一對額外的圓括號。

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

括號

寧缺毋濫的使用括號。
除非是用於實現行連接, 否則不要在返回語句或條件語句中使用括號。 不過在元組兩邊使用括號是可以的。

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的寬度可能是2、4或8個字符,甚至是其他更離譜的值,用Tab來表示縮進對Python代碼來說可能是一場災難。
  • 和語法相關的每一層縮進都用4個空格來表示。
  • 除了首行之外的其余各行都應該在正常的縮進寬度上再加上4個空格。
  • 函數和類的定義,代碼前后都要用兩個空行進行分隔。
  • 在同一個類中,各個方法之間應該用一個空行進行分隔。
  • 二元運算符的左右兩側應該保留一個空格,而且只要一個空格就好。

標識符命名

PEP 8倡導用不同的命名風格來命名Python中不同的標識符,以便在閱讀代碼時能夠通過標識符的名稱來確定該標識符在Python中扮演了怎樣的角色(在這一點上,Python自己的內置模塊以及某些第三方模塊都做得並不是很好)。

  • 變量、函數和屬性應該使用小寫字母來拼寫,如果有多個單詞就使用下划線進行連接。
  • 類中受保護的實例屬性,應該以一個下划線開頭。
  • 類中私有的實例屬性,應該以兩個下划線開頭。
  • 類和異常的命名,應該每個單詞首字母大寫。
  • 模塊級別的常量,應該采用全大寫字母,如果有多個單詞就用下划線進行連接。
  • 類的實例方法,應該把第一個參數命名為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風格指南

谷歌開源項目風格規范

慣例


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM