- 取得連結
- X
- 以電子郵件傳送
- 其他應用程式
程式語言:python
Package:pep8
官方文件
簡介:程式碼的風格指南
表達式和語句中的空格
行內註解(Inline Comments)
行內註解是和代碼語句寫在一行內的註解
PEP8 Python 编码规范整理
python代码风格指南:pep8 中文翻译
Package:pep8
官方文件
簡介:程式碼的風格指南
檢查方法
# 常用 pep8 --first optparse.py # 顯示程式碼 pep8 --show-source --show-pep8 testsuite/E40.py # 統計錯誤 pep8 --statistics -qq Python-2.5/Lib
忽略的條件
- 採用時會讓代碼更難閱讀,甚至對於習慣閱讀 PEP8 的人也是如此。
- 需要和周圍的代碼保持一致性,但這些代碼違反了指南中的風格
—— 儘管這可能也是一個收拾別人爛攤子的機會 - 若是有問題的某段代碼早於引入指南的時間,那麼沒有必要去修改這段代碼。
- 代碼需要和更舊版本 Python 保持兼容,而舊版本 Python 不支持所推薦的風格
代碼排版 (Code lay-out)
縮排 (Indentation)
請使用四個空格當作縮排
兩種縮排方式
錯誤方式
續行可違反4 空格
多行 if 語句
PEP8 未明確規定
以下幾種方法皆可行的,但不僅僅只限於這幾種方法
多行結束右圓/方/花括號
上一行的縮排對齊
每行最大長度 (Maximum Line Length)
建議第一種,除非第一種無法採用
邏輯運算符號位置
正確方式
錯誤方式
空行 (Blank line)
請使用四個空格當作縮排
兩種縮排方式
- Python 隱式續行,即圓括號、方括號和花括號換行不影響,而垂直對齊之
- 懸掛縮排 (hanging indent)
- 第一行不應該包括參數
- 續行中需要再縮排一級以便清楚表示
# 同左括號對齊 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) # 懸掛縮排需要多縮進一級 foo = long_function_name( var_one, var_two, var_three, var_four)
錯誤方式
# 未對齊左括號 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)
續行可違反4 空格
# 懸掛縮排可以不採用 4 空格的縮排方法 foo = long_function_name( var_one, var_two, var_three, var_four)
多行 if 語句
PEP8 未明確規定
以下幾種方法皆可行的,但不僅僅只限於這幾種方法
# 不採用額外縮排 if (this_is_one_thing and that_is_another_thing): do_something() # 增加一行註解,在編輯器中顯示時能有所區分 if (this_is_one_thing and that_is_another_thing): # 註解隔開 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',
)
和第一行的第一個字符對齊
my_list = [
1, 2, 3,
4, 5, 6,
]
result = some_function_that_takes_arguments(
'a', 'b', 'c',
'd', 'e', 'f',
)
每行最大長度 (Maximum Line Length)
- 限制 79 個字符長度
- 文檔字符串 (docstring) 或 註解 則限制在 72 個字符長度
- 便於比較兩個版本的代碼
建議第一種,除非第一種無法採用
- 利用 Python 圓括號、方括號和花括號中的隱式續行
- 使用反斜槓,例如,with 語句不能採用隱式續行
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())
邏輯運算符號位置
正確方式
# 放在前面 income = (gross_wages + taxable_interest + (dividends - qualified_dividends) - ira_deduction - student_loan_interest)
錯誤方式
# 放在後面 income = (gross_wages + taxable_interest + (dividends - qualified_dividends) - ira_deduction - student_loan_interest)
空行 (Blank line)
- 2 個空行
- 分隔 top function 和 class
- 1 個空行
- 分隔 class 的 method
- 需用在刀口上
- 分隔不同的 function set
- 分割不同的邏輯塊
def function1(): pass def function2(): pass class class1(): def mthod1(): pass def mthod2(): pass
Imports
分行寫
位置
用空行隔開
absolute imports
明確的相對 imports
當處理複雜的 package layouts 時
import class
命名衝突
直接 import 模塊
避免使用 from <module> import *
可能會造成互相覆蓋的情況
# 分開寫 import os import sys # 不要像下面一樣寫在一行 import sys, os這樣寫也是可以的
from subprocess import Popen, PIPE
位置
- 代碼文件的開頭
- module 註解和文檔字符串之後
- 全局變數 (globals) 和 常數 (constants) 宣告之前
用空行隔開
- 標準庫 imports
- 相關第三方 imports
- 本地應用/庫的特定 imports
absolute imports
import mypkg.sibling from mypkg import sibling from mypkg.sibling import example
明確的相對 imports
當處理複雜的 package layouts 時
from . import sibling from .sibling import example
import class
from myclass import MyClass from foo.bar.yourclass import YourClass
命名衝突
直接 import 模塊
import myclass import foo.bar.yourclass
避免使用 from <module> import *
可能會造成互相覆蓋的情況
Module Level 雙底線變數 (dunder)
- 像是 __all__ , __author__ , __version__
- module docstring 之後
- import 之前
- __future__
- 任何 code 之前
- 除了 docstrings 以外
"""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
字符串引用 (String Quotes)
- String 宣告
- 不混用單引號 或 雙引號
- 單一規則堅持使用
- 三引號只使用雙引號(即是 """ 而不是 ''' )
表達式和語句中的空格
(Whitespace in Expressions and Statements)
避免使用過多的空白
方括號,圓括號和花括號之後
slice 操作
冒號和二元運算符是一樣的
錯誤方式
傳遞函數參數括號之前
在索引左括號之前
賦值
正確方式
錯誤方式
運算符
錯誤方式
關鍵字參數
不要在其周圍使用空格
正確方式
錯誤方式
函數參數註解
錯誤方式
禁止多行語句寫在一行
正確方式
錯誤方式
有時候可將短的 if/for/while 寫在一行
但對於有多個分句的語句永遠不要這樣做
錯誤方式
錯誤方式
方括號,圓括號和花括號之後
Yes: spam(ham[1], {eggs: 2}) No: spam( ham[ 1 ], { eggs: 2 } )逗號,分號或冒號之前
Yes: if x == 4: print x, y; x, y = y, x No: if x == 4 : print x , y ; x , y = y , x
slice 操作
冒號和二元運算符是一樣的
- 左右兩邊保留相同數量的空格
- 參數被省略時,應該也忽略空格
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]
錯誤方式
ham[lower + offset:upper + offset] ham[1: 9], ham[1 :9], ham[1:9 :3] ham[lower : : upper] ham[ : upper]
傳遞函數參數括號之前
Yes: spam(1) No: spam (1)
在索引左括號之前
Yes: dct['key'] = lst[index] No: dct ['key'] = lst [index]
賦值
正確方式
x = 1 y = 2 long_variable = 3
錯誤方式
x = 1 y = 2 long_variable = 3
運算符
- 不同優先級的運算符
- 優先級較低的運算符增加空白
- 不超過 1 個空格
- 兩側的空白數量一樣
i = i + 1 submitted += 1 x = x*2 - 1 hypot2 = x*x + y*y c = (a+b) * (a-b)
錯誤方式
i=i+1 submitted +=1 x = x * 2 - 1 hypot2 = x * x + y * y c = (a + b) * (a - b)
關鍵字參數
不要在其周圍使用空格
正確方式
def complex(real, imag=0.0): return magic(r=real, i=imag)
錯誤方式
def complex(real, imag = 0.0): return magic(r = real, i = imag)
函數參數註解
- 在 : 後使用一個空格
- 在 -> 兩側各使用一個空格
def munge(input: AnyStr): ... def munge() -> AnyStr: ...
錯誤方式
def munge(input:AnyStr): ... def munge()->PosInt: ...
禁止多行語句寫在一行
正確方式
if foo == 'blah': do_blah_thing() do_one() do_two() do_three()
錯誤方式
if foo == 'blah': do_blah_thing() do_one(); do_two(); do_three()
有時候可將短的 if/for/while 寫在一行
但對於有多個分句的語句永遠不要這樣做
錯誤方式
if foo == 'blah': do_blah_thing() for x in lst: total += x while t < 10: t = delay()
錯誤方式
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()
句尾逗號 (When to use trailing commas )
Tuple
正確方式
錯誤方式
不同行的 list/tuple 元素
可加入逗號,但同一行的情況下只是多餘的
正確方式
錯誤方式
正確方式
FILES = ('setup.cfg',)
錯誤方式
FILES = 'setup.cfg',
不同行的 list/tuple 元素
可加入逗號,但同一行的情況下只是多餘的
正確方式
FILES = [ 'setup.cfg', 'tox.ini', ] initialize(FILES, error=True, )
錯誤方式
FILES = ['setup.cfg', 'tox.ini',] initialize(FILES, error=True,)
註解 (Comments)
- 和代碼矛盾的註解還不如沒有
- 當代碼有改動時,優先更改註解使其保持最新
- 註解應該是完整的多個句子
- 註解是一個短語或一個句子,其首字母應該大寫
除非是小寫字母開頭的 identifier(永遠不要更改 identifier 的大小寫) - 註解很短,結束的句號可以被忽略。
- 塊註解通常由一段或幾段完整的句子組成,每個句子都應該以句號結束
- 你應該在句尾的句號後再加上 2 個空格
- 來自非英語國家的 Python 程序員們,請使用英文來寫註解
除非你 120% 確定你的代碼永遠不會被不懂你所用語言的人閱讀到
- 寫在對應代碼之前
- 和對應代碼有同樣的縮排
- 以 # 和一個空格開頭(除非該文本是在註解內縮排對齊的)
- 空行用只含有單個 # 的一行隔開
# 這是一個註解 # # 這是第二個註解
行內註解(Inline Comments)
行內註解是和代碼語句寫在一行內的註解
- 儘量少用
- 和代碼語句之間有 2 個空格的間隔
- 以 # 和一個空格開始
x = x + 1 # Increment x有意義
x = x + 1 # Compensate for border
文檔字符串 (Documentation Strings)
- 所有的 public module,function,class 和 method 都應有 doc strings
- 非公共方法,文檔字符串不是必要的,但應留有註解說明功能
- 該註解應當出現在 def 的下一行
- 以單行 """ 結尾,不能有其他字符
- 只有單行的, """ 應寫在同一行
"""Return a foobang Optional plotz says to frobnicate the bizbaz first. """
特別命名規範
- _single_leading_underscore
- 單個下劃線開頭表示「內部使用」的弱標誌
- E.g. "from M import *" 不會 import 下劃線開頭的對象
- single_trailing_underscore_
- 單個下劃線結尾用來避免和 Python 關鍵詞產生衝突
例如: Tkinter.Toplevel(master, class_='ClassName') - __double_leading_underscore
- 雙下劃線開頭的命名,class 屬性將觸發命名修飾,被重新命名之
(在FooBar類中,"__boo" 命名會被修飾成 "_FooBar__boo") - __double_leading_and_trailing_underscore__
- 雙下劃線開頭和結尾的命名風格為 magic property
E.g. ``__init__``, ``__import__`` 或 ``__file__``
請依照文檔描述來使用這些命名,千萬不要自己發明
規範性:命名約定 (Prescriptive: Naming Conventions)
需要避免的命名 (Names to Avoid)
單個字符的變數
不要使用和數字無法區別開的,像是小寫 l,大寫 O 或大寫 I
module/package/Class/Type 命名
全域變量命名 (Global Variable Names)
函數命名 (Function Names)
函數和方法參數 (Function and method arguments)
常數 (Constants)
繼承設計 (Designing for inheritance)
公開和內部接口 (Public and internal interfaces)
單個字符的變數
不要使用和數字無法區別開的,像是小寫 l,大寫 O 或大寫 I
module/package/Class/Type 命名
- module
- 對應到文件名
- 命名短
- 全小寫
- 可使用下劃線
- C/C++ module 以下劃線開頭(e.g. _sociket)
- package
- 命名短
- 全小寫
- 不應使用下劃線
- Class
- 單詞字母大寫(CapWords)
- Type
- 單詞字母大寫(CapWords)
- 後綴建議加上 _co or _contra
from typing import TypeVar VT_co = TypeVar('VT_co', covariant=True) KT_contra = TypeVar('KT_contra', contravariant=True)
- 同 class 命名
- 加上 Error 的前綴
全域變量命名 (Global Variable Names)
- 假設只在一個 module 中
- 避免 from M import *
- 使用 __all__ 機制
- 採用下劃線前綴的舊約定來命名非公開全域變量
函數命名 (Function Names)
- 全小寫
- 可使用下劃線提高可讀性
函數和方法參數 (Function and method arguments)
- instance 第一參數必定是 self
- method 第一參數必定是 cls
- 事實上,這只是約定俗成的名字,不一定需是 self or cls
- 和保留關鍵字衝突時,使用下劃線結尾的命名,如 class_ 或 使用同義詞
- 全小寫單詞
- 使用下劃線提高可讀性
- 一個下劃線開頭
- 只對非公開方法和變數命名
- 兩個下劃線開頭
- 觸發 Python 的命名修飾機制
- 只用來避免與子類屬性的命名衝突
常數 (Constants)
- 在 module 定義的
- 全大寫
- 用下劃線將單詞分開
- MAX_OVERFLOW 和 TOTAL
繼承設計 (Designing for inheritance)
- 決定 class 的 method 和 property 應是公開的還是非公開的
- 有疑慮的話,請選擇非公開的;因為之後將非公開屬性變為公開屬性要容易些
- 公開屬性
- 開頭不該有下劃線
- 和保留關鍵字衝突,後綴加上一個下劃線
- 僅公開屬性名字,不要公開複雜的調用或設值方法
- 使用 @property
- 對計算量大的運算避免使用 @property
- 非公開屬性
- 前綴加上兩個下劃線並且結尾處沒有下劃線,會觸發 Python 命名修飾算法
公開和內部接口 (Public and internal interfaces)
- module 應該在 __all__ 中明確定義公開的 API
- __all__ = [] 表示 module 無公開 API
- 即使正確設置 __all__
內部接口如 package,module,class,function,property 或其他命名,也應以一個下劃線開頭
設計建議 (Programming Recommendations)
- 兼顧所有 python 的平台
- 像是字串處理 a += b or a = a + b,在 CPython 只適用部分 type
使用 ''.join() 取代 - 與 None 比較
- 使用 is 或 is not
- 禁止使用 ==
- 檢查是否不為 None,必須使用 is not
以免跟 空 list 或其他物件搞混Yes: if x is not None No: if x
-
把 not 放在 is 後面
正確方式if foo is not None:
錯誤方式if not foo is None:
- 若 class 有大小之分
- 建議實現 __eq__ , __ne__ , __lt__ , __le__ , __gt__ , __ge__
而不是另外寫 method 實現 - 宣告 fucntion
- 總是使用 def,debug 訊息較為明確
正確方式def f(x): return 2*x
錯誤方式f = lambda x: 2*x
- Exception
- 清楚明白什麼錯誤該怎麼處理
- 嘗試描述什麼造成的問題
- 格式為 raise X from Y 且保留原始的 traceback
- 使用者需知道發生什麼錯誤
-
try/except 儘可能簡單明瞭
正確方式try: value = collection[key] except KeyError: return key_not_found(key) else: return handle_value(value)
錯誤方式try: # 太廣泛 return handle_value(collection[key]) except KeyError: # handle_value() 也可能有 KeyError return key_not_found(key)
- 自定 Exception
- 繼承自 Exception 而不是 BaseException
- Class naming 加入前綴 Error
- bare except:
- 儘可能不只使用 except:,而是指定 Exception
try: import platform_specific_module except ImportError: platform_specific_module = None
- 因 SystemExit 和 KeyboardInterrupt 皆滿足,導致很難用 Control-C 中斷
- 用 except Exception: 取代
- 等同 except BaseException:
- 儘量使用 with 呼叫 local resource
- 像是讀寫檔,確保即時清理,try/finally 也是可以
- 明確知道出 with 要關閉的是什麼
正確方式with conn.begin_transaction(): do_stuff_in_transaction(conn)
錯誤方式with conn: do_stuff_in_transaction(conn)
後者會將 conn 關掉,前者則是將 begin_transaction 回傳的關掉 - 若有 return 的 function,若無值也需回傳 None
正確方式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)
錯誤方式def foo(x): if x >= 0: return math.sqrt(x) def bar(x): if x < 0: return return math.sqrt(x)
- string
- 使用自帶的 methods 而不是 string module 的 function
- 別在結尾加很多空白,因為肉眼看不見,但處理會有問題,像互相比較
- 使用 ''.startswith() 跟 ''.endswith() 檢查開頭與結尾,較簡潔又少錯誤
Yes: if foo.startswith('bar'): No: if foo[:3] == 'bar':
- 比較 type 需用 isinstance()
Yes: if isinstance(obj, int): No: if type(obj) is type(1):
- 對於 sequences, (strings, lists, tuples),空的就是 False
Yes: if not seq: if seq: No: if len(seq): if not len(seq):
- 別對 boolean 使用 ==
Yes: if greeting: No: if greeting == True: Worse: if greeting is True:
參考
Python代碼風格指南(一)代碼設計(PEP8中文翻譯)PEP8 Python 编码规范整理
python代码风格指南:pep8 中文翻译
留言
張貼留言