Skip to content

[Python] 值得參考的 Coding Style 整理筆記

Last Updated on 2021-07-25 by Clay

最近有個比較大一點的個人專案準備收尾,並準備交接給朋友接手,看他後續想要新增什麼樣的功能。有鑑於此,我開始考慮是否將程式碼做個徹底的整理。

其實我覺得重要的是 Coding Style 要從頭到尾保持一致, ...... 有時候前後寫法不同會讓人看得很痛苦,真的。另外就是需要跟團隊(我的團隊只有兩人啊哈哈)溝通好彼此撰寫的程式碼風格。

不過,既然決定了要徹底整理自己的程式碼給別人接手,那麼我想參考高手、大神的建議的風格規範總是好的。本篇文章主要就是紀錄一份名為『Google 開源專案風格指南』的筆記,讓我能按照自己整理的版面,去參照、進而修改自己的專案,使其符合這開源專案風格指南所建議的風格。

在此,非常感謝原作者:
- Amit Patel
- Antoine Picard
- Eugene Jhong
- Jeremy Hylton
- Matt Smart
- Mike Shields


以及中文版風格指南的翻譯:
- guoqiao
- xuxinkun

那麼,以下便是 Google 開源專案風格指南中所定義的 Python 風格規範:

規範解釋
分號不要在行尾加分號,也不要用分號將兩條命令放在同一行
行長度每行不超過 80 個字符
括號寧缺勿濫地使用括號
縮排 使用 4 個空格來縮進代碼
空行頂級定義之間空兩行,方法定義之間空一行
空格按照標準的排版規範來使用標點兩邊的空格
Shebang大部分的 .py 文件不必以 #! 作為文件開頭
註釋確保對模塊、函數、方法和行內註釋使用正確的風格
如果一個類不繼承自其它類,就顯式地從 Object 繼承
字串盡量使用 % 或格式化方法格式化字串
文件和 Sockets在文件和 Sockets 結束時,顯式地關閉
TODO 註釋為臨時程式碼使用 TODO 註釋
導入格式每個導入應該獨佔一行
語句每個語句應該獨佔一行
訪問控制對於瑣碎不重要的訪問函數,使用全域變數來取代
命名應避免單字符、Dash、雙下底線等命名方式
Main即使是腳本文件,也應是可導入的

以下的介紹會有條列式的敘述、以及範例程式。唯有粗體字為我個人見解,可能有誤,酌情參考。


分號

不要在行尾加分號,也不要用分號將兩條命令放在同一行
# No
a = 1;
b = 1; c = a + b

# Yes
a = 1
b = 1
c = a + b



行長度

每行不超過 80 個字符

這很好理解,過長的程式碼會造成閱讀理解上的障礙。


括號

寧缺勿濫地使用括號
  • 不要在函式返回時使用
  • 不要在條件式中使用
  • 元組 (Tuple) 可以使用
# No
return (x, y)
if (x and y): ...


# Yes
return x, y
if x and y



我個人傾向於這是習慣問題 ...... 不過我同意不要亂加括號程式碼看起來比較清楚。


縮排

使用 4 格空白來進行縮排
  • 不要使用 TAB
  • 換行時元素對齊
# No
id2value = {
    1: 10,
2: 20,
3: 30,
}


# Yes
id2value = {
    1: 10,
    2: 20,
    3: 30,
}



由於 Python 嚴格地需要使用『縮排』來區分程式碼區塊,比方說 For 迴圈或是函式,故夾雜使用空白或 TAB 會導致 Python 直譯器發生『縮排錯誤』的報錯。

另外元素對齊,是為了美觀、也是為了減輕閱讀者的負擔。


空行

頂級定義之間空兩行,方法定義之間空一行
  • 函式 (Function) 之間空兩行
  • 類 (Class) 之間空兩行
  • 方法 (Method) 之間空一行
  • 某些地方覺得適合,就空一行 (?)
# No
def x():
    ...
def y():
    ...

# Yes
def x():
    ...


def y():
    ...


# No
class x:
    def __init__(self):
        ...


    def process(self):
        ...

# Yes
class x:
    def __init__(self):
        ...

    def process(self):
        ...



這部份就是統一格式罷了,最大的問題是『覺得適合就空一行』這個敘述非常含糊 —— 我自己是在遇到迴圈或條件判斷時會在上方空一行。


空格

按照標準的排版規範來使用標點兩邊的空格
  • 不要在逗號、分號、冒號前方加空格,但後方則需要
  • 操作符左右都應加上空格
# No
a,b,c=1,2,3



# Yes
a, b, c = 1, 2, 3



Shebang

大部分的 .py 文件不必以 #! 作為文件開頭

註釋

確保對模塊、函數、方法和行內註釋使用正確的風格
  • Python 慣例使用三重雙引號 """ 來當作文檔字串符
  • 註釋可描述每個參數名稱與功用 (Args)
  • 註釋可描述返回值的類型與語義 (Returns)
  • 註釋可描述函式接口的所有異常 (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


  • 要解釋程式碼中帶有技巧性的部份,使用 # 註釋於行尾
  • 註釋應距離程式碼至少 2 個空格
# No
if i & (i-1) == 0:# true iff i is a power of 2


# Yes
if i & (i-1) == 0: # true iff i is a power of 2



如果一個類不繼承自其它類,就顯式地從 Object 繼承
  • 繼承 Object 是為了使屬性 (Properties) 正常運作
# No
class SampleClass:
    pass


# Yes
class SampleClass(object):
    pass



字串

盡量使用 % 或格式化方法格式化字串
  • 避免使用 + 或是 += 來增加字符串,會創建不必要的臨時對象
  • 改將字串加入列表,使用 join() 連接字串
# No
string = ''
for item in items:
    string += item


# Yes
string = []
for item in items:
    string.append(item)

string = ''.join(item)



文件和 Sockets

在文件和 Sockets 結束時,顯式地關閉
  • 不關閉會消耗系統資源
  • 不同 Python 記憶體管理技術不保證是同樣實現
  • 推薦使用 with 管理文件

這是我經常會犯的錯誤 ...... 在仔細查看這篇文檔之前沒想過這麼危險。


TODO 註釋

為臨時程式碼使用 TODO 註釋
  • TODO 註釋應於開頭包含 "TODO" 字串
  • 應該留有開發者名字、Email
  • 解釋要做什麼
# TODO([email protected]): Use a "*" here for string repetition.
# TODO(Zeke) Change this to use relations.



導入格式

每個導入應該獨佔一行
# No
import os, sys

# Yes
import os
import sys


  • 導入應於文件開頭
  • 導入應於模塊註釋和文檔字串後
  • 導入應於程式碼前

導入應有固定順序:

  1. 標準模組導入
  2. 第三方模組導入
  3. 其他

導入順序按字母排序:

import foo
from foo import bar
from foo.bar import baz
from foo.bar import Quux
from Foob import ar



語句

每個語句應該獨佔一行
# No
if foo: bar(foo)
else: baz(foo)

try: bar(foo)
except ValueError: baz(foo)

# Yes
if foo:
    bar(foo)
else:
    baz(foo)

try:
    bar(foo)
except ValueError:
    baz(foo)



訪問控制

對於瑣碎不重要的訪問函數,使用全域變數來取代

這邊文檔中只寫著要嘗試接受 Pythonic 哲學 XDDD

我想,除非真的太過消耗效能,否則直接使用全域變數其實是很方便的。


命名

應避免單字符、Dash、雙下底線等命名方式
  • 除了計數與迭代,否則不應使用單字符
  • 不應使用 dash (-) 符號,該使用於套件或模組名稱
  • __xx__ 為 Python 保留名稱

推薦規範:

TypePublicInternal
Modules (模組)lower_with_underlower_with_under
Packages (套件)lower_with_under
Classes (類)CapWords_CapWords
Exceptions (例外)CapWords
Functions (函式)lower_with_under()_lower_with_under()
Global/Class Constant (常數)CAPS_WITH_UNDER_CAPS_WITH_UNDER
Global/Class Variables (變數)lower_with_under_lower_with_under
Instance Variableslower_with_under_lower_with_under (protect)
__lower_with_under(private)
Method Nameslower_with_under()_lower_with_under() (protect)
__lower_with_under() (private)
Function/Method Parameters (參數)lower_with_under
Local Variableslower_with_under

Main

即使是腳本文件,也應是可導入的
# Yes
def main():
    ...

if __name__ == '__main__':
    main()



慚愧 ..... 我有很多只是單純執行、測試某些元件的程式,都像是腳本一樣由上到下寫。

不過真的開發要使用的功能,倒是會好好遵守著這樣的格式規範。


References

Tags:

1 thought on “[Python] 值得參考的 Coding Style 整理筆記”

  1. Pingback: 【程式優化】Google Coding Style - 01 | variable/ function python 等命名原則 - 嗡嗡的隨手筆記

Leave a Reply