最近半年都在斷斷續續地學習使用python,基本語法和基本編程思想都差不多瞭解了。就是一知半解的意思啦~~最近在着手寫一些工具了。是時候好好學習一下規範一點的代碼規範了。
今天轉載一下oschina的python碼風格pep8的翻譯文章。希望對自己以後的編碼也能規範一點。
代碼佈局
縮進
每級縮進用4個空格。
括號中使用垂直隱式縮進或使用懸掛縮進。後者應該注意第一行要沒有參數,後續行要有縮進。
Yes
# 對準左括號 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)
No
# 不使用垂直對齊時,第一行不能有參數。 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語句跨行時,兩個字符關鍵字(比如if)加上一個空格,再加上左括號構成了很好的縮進。後續行暫時沒有規定,至少有如下三種格式,建議使用第3種。
# 沒有額外縮進,不是很好看,個人不推薦. 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() # 額外添加縮進,推薦。 # Add some extra indentation on the conditional continuation line. if (this_is_one_thing and that_is_another_thing): do_something()
右邊括號也可以另起一行。有兩種格式,建議第2種。
# 右括號不回退,個人不推薦 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', )
空格或Tab?
空格是首選的縮進方法。
Tab僅僅在已經使用tab縮進的代碼中爲了保持一致性而使用。
Python 3中不允許混合使用Tab和空格縮進。
Python 2的包含空格與Tab和空格縮進的應該全部轉爲空格縮進。
Python2命令行解釋器使用-t選項時有非法混合Tab和空格的情況會告警。當使用-tt警告提升爲錯誤。強烈推薦這些選項!另外個人推薦pep8和autopep8模塊。
最大行寬
限制所有行的最大行寬爲79字符。
文本長塊,比如文檔字符串或註釋,行長度應限制爲72個字符。
多數工具默認的續行功能會破壞代碼結構,使它更難理解,不推薦使用。但是超過80個字符加以提醒是必要的。一些工具可能根本不具備動態換行功能。
一些團隊強烈希望更長的行寬。如果能達成一致,可以從從80提高到100個字符(最多99個字符)增加了標稱線的長度,不過依舊建議文檔字符串和註釋保持在72的長度。
Python標準庫比較保守,限制行寬79個字符(文檔字符串/註釋72)。
續行的首選方法是使用小括號、中括號和大括號反斜線仍可能在適當的時候。其次是反斜槓。比如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())
類似的還有assert。
注意續行要儘量不影響可讀性。比如通常在二元運算符之後續行:
class Rectangle(Blob): def __init__(self, width, height, color='black', emphasis=None, highlight=0): if (width == 0 and height == 0 and color == 'red' and emphasis == 'strong' or highlight > 100): raise ValueError("sorry, you lose") if width == 0 and height == 0 and (color == 'red' or emphasis is None): raise ValueError("I don't think so -- values are %s, %s" % (width, height)) Blob.__init__(self, width, height, color, emphasis, highlight)
空行
兩行空行分割頂層函數和類的定義。
類的方法定義用單個空行分割。
額外的空行可以必要的時候用於分割不同的函數組,但是要儘量節約使用。
額外的空行可以必要的時候在函數中用於分割不同的邏輯塊,但是要儘量節約使用。
Python接 contol-L作爲空白符;許多工具視它爲分頁符,這些要因編輯器而異。
源文件編碼
在覈心Python發佈的代碼應該總是使用UTF-8(ASCII在Python 2)。
ASCII文件(Python 2)或UTF-8(Python 3)不應有編碼聲明。
標準庫中非默認的編碼應僅用於測試或當註釋或文檔字符串,比如包含非ASCII字符的作者姓名,儘量使用\x , \u , \U , or \N。
Python 3.0及以後版本,PEP 3131可供參考,部分內容如下:在Python標準庫必須使用ASCII標識符,並儘量只使用英文字母。此外字符串和註釋也必須用ASCII。唯一的例外是:(a)測試非ASCII的功能,和(b)作者的名字不是拉丁字母。
導入
導入在單獨行
import os import sys from subprocess import Popen, PIPE
No:
import os, sys
導入始終在文件的頂部,在模塊註釋和文檔字符串之後,在模塊全局變量和常量之前。
導入順序如下:標準庫進口,相關的第三方庫,本地庫。各組的導入之間要有空行。
相關的all放在導入之後。
推薦絕對路徑導入,因爲它們通常更可讀,而且往往是表現更好的(或至少提供更好的錯誤消息。
import mypkg.sibling from mypkg import sibling from mypkg.sibling import example
在絕對路徑比較長的情況下,也可以使用相對路徑:
from . import sibling from .sibling import example
Python 3中已經禁止隱式的相對導入。
導入類的方法:
from myclass import MyClass from foo.bar.yourclass import YourClass
如果和本地名字有衝突:
import myclass import foo.bar.yourclass
禁止使用通配符導入。
通配符導入(from <module> import *)應該避免,因爲它不清楚命名空間有哪些名稱存,混淆讀者和許多自動化的工具。唯一的例外是重新發布對外的API時可以考慮使用。
字符串引用
Python中單引號字符串和雙引號字符串都是相同的。注意儘量避免在字符串中的反斜槓以提高可讀性。
根據PEP 257, 三個引號都使用雙引號。
表達式和語句中的空格
強制要求
括號裏邊避免空格
# 括號裏邊避免空格 # 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
索引操作中的冒號當作操作符處理前後要有同樣的空格(一個空格或者沒有空格,個人建議是沒有。
# Yes 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] # No ham[lower + offset:upper + offset] ham[1: 9], ham[1 :9], ham[1:9 :3] ham[lower : : upper] ham[ : upper]
函數調用的左括號之前不能有空格
# Yes spam(1) dct['key'] = lst[index] # No spam (1) dct ['key'] = lst [index]
賦值等操作符前後不能因爲對齊而添加多個空格
# Yes x = 1 y = 2 long_variable = 3 # No x = 1 y = 2 long_variable = 3
其他建議
二元運算符兩邊放置一個空格:
涉及 =、符合操作符 ( += , -=等)、比較( == , < , > , != , <> , <= , >= , in , not in , is , is not )、布爾( and , or , not )。
優先級高的運算符或操作符的前後不建議有空格。
# Yes i = i + 1 submitted += 1 x = x*2 - 1 hypot2 = x*x + y*y c = (a+b) * (a-b) # No i=i+1 submitted +=1 x = x * 2 - 1 hypot2 = x * x + y * y c = (a + b) * (a - b)
關鍵字參數和默認值參數的前後不要加空格
# Yes def complex(real, imag=0.0): return magic(r=real, i=imag) # No def complex(real, imag = 0.0): return magic(r = real, i = imag)
函數註釋中,=前後要有空格,冒號和"->"的前面無空格,後面有空格。
# Yes def munge(input: AnyStr): def munge(sep: AnyStr = None): def munge() -> AnyStr: def munge(input: AnyStr, sep: AnyStr = None, limit=1000): # No def munge(input: AnyStr=None): def munge(input:AnyStr): def munge(input: AnyStr)->PosInt:
通常不推薦複合語句(Compound statements: 多條語句寫在同一行)。
# Yes if foo == 'blah': do_blah_thing() do_one() do_two() do_three() # No if foo == 'blah': do_blah_thing() do_one(); do_two(); do_three()
儘管有時可以在if/for/while 的同一行跟一小段代碼,但絕不要跟多個子句,並儘量避免換行。
# No if foo == 'blah': do_blah_thing() for x in lst: total += x while t < 10: t = delay()
更不是:
# No 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()
註釋
與代碼自相矛盾的註釋比沒註釋更差。修改代碼時要優先更新註釋!
註釋是完整的句子。如果註釋是斷句,首字母應該大寫,除非它是小寫字母開頭的標識符(永遠不要修改標識符的大小寫)。
如果註釋很短,可以省略末尾的句號。註釋塊通常由一個或多個段落組成。段落由完整的句子構成且每個句子應該以點號(後面要有兩個空格)結束,並注意斷詞和空格。
非英語國家的程序員請用英語書寫你的註釋,除非你120%確信代碼永遠不會被不懂你的語言的人閱讀。
註釋塊
註釋塊通常應用在代碼前,並和這些代碼有同樣的縮進。每行以 '# '(除非它是註釋內的縮進文本,注意#後面有空格)。
註釋塊內的段落用僅包含單個 '#' 的行分割。
行內註釋
慎用行內註釋(Inline Comments) 節儉使用行內註釋。 行內註釋是和語句在同一行,至少用兩個空格和語句分開。行內註釋不是必需的,重複羅嗦會使人分心。不要這樣做:
x
=
x
+
1
# Increment x
但有時很有必要:
x
=
x
+
1
# Compensate for border
文檔字符串
文檔字符串的標準參見:PEP 257。
爲所有公共模塊、函數、類和方法書寫文檔字符串。非公開方法不一定有文檔字符串,建議有註釋(出現在 def 行之後)來描述這個方法做什麼。
更多參考:PEP 257 文檔字符串約定。注意結尾的 """ 應該單獨成行,例如:
"""Return a foobang Optional plotz says to frobnicate the bizbaz first. """
單行的文檔字符串,結尾的 """ 在同一行。
版本標籤
版本註記 (Version Bookkeeping)
如果你必須在源文件中包含git、Subversion、CVS或RCS crud信息,放置在模塊的文檔字符串之後,任何其他代碼之前,上下各用一個空行:
version__
=
"$Revision$"
# $Source$
命名約定
Python庫的命名約定有點混亂,不可能完全一致。但依然有些普遍推薦的命名規範的。新的模塊和包 (包括第三方的框架) 應該遵循這些標準。對不同風格的已有的庫,建議保持內部的一致性。
最重要的原則
用戶可見的API命名應遵循使用約定而不是實現。
描述:命名風格
有多種命名風格:
b(單個小寫字母)
B(單個大寫字母)
lowercase(小寫串)
lower_case_with_underscores(帶下劃線的小寫)
UPPERCASE(大寫串)
UPPER_CASE_WITH_UNDERSCORES(帶下劃線的大寫串)
CapitalizedWords(首字母大寫的單詞串或駝峯縮寫)
注意: 使用大寫縮寫時,縮寫使用大寫字母更好。故 HTTPServerError 比 HttpServerError 更好。
mixedCase(混合大小寫,第一個單詞是小寫)
Capitalized_Words_With_Underscores(帶下劃線,首字母大寫,醜陋)
還有一種風格使用短前綴分組名字。這在Python中不常用, 但出於完整性提一下。例如,os.stat()返回的元組有st_mode, st_size, st_mtime等等這樣的名字(與POSIX系統調用結構體一致)。
X11庫的所有公開函數以X開頭, Python中通常認爲是不必要的,因爲屬性和方法名有對象作前綴,而函數名有模塊名爲前綴。
下面講述首尾有下劃線的情況:
_single_leading_underscore:(單前置下劃線): 弱內部使用標誌。 例如"from M import " 不會導入以下劃線開頭的對象。
single_trailing_underscore_(單後置下劃線): 用於避免與 Python關鍵詞的衝突。 例如:
Tkinter.Toplevel(master,
class_
=
'ClassName'
)
__double_leading_underscore(雙前置下劃線): 當用於命名類屬性,會觸發名字重整。 (在類FooBar中,__boo變成 _FooBar__boo)。
__double_leading_and_trailing_underscore__(雙前後下劃線):用戶名字空間的魔法對象或屬性。例如:__init__ , __import__ or __file__,不要自己發明這樣的名字。
命名約定規範
避免採用的名字
決不要用字符'l'(小寫字母el),'O'(大寫字母oh),或 'I'(大寫字母eye) 作爲單個字符的變量名。一些字體中,這些字符不能與數字1和0區別。用'L' 代替'l'時。
包和模塊名
模塊名要簡短,全部用小寫字母,可使用下劃線以提高可讀性。包名和模塊名類似,但不推薦使用下劃線。
模塊名對應到文件名,有些文件系統不區分大小寫且截短長名字,在 Unix上不是問題,但當把代碼遷移到 Mac、Windows 或 DOS 上時,就可能是個問題。當然隨着系統的演進,這個問題已經不是經常出現。
另外有些模塊底層用C或C++ 書寫,並有對應的高層Python模塊,C/C++模塊名有一個前置下劃線 (如:_socket)。
類名
遵循CapWord。
接口需要文檔化並且可以調用時,可能使用函數的命名規則。
注意大部分內置的名字是單個單詞(或兩個),CapWord只適用於異常名稱和內置的常量。
異常名
如果確實是錯誤,需要在類名添加後綴 "Error"。
全局變量名
變量儘量只用於模塊內部,約定類似函數。
對設計爲通過 "from M import " 來使用的模塊,應採用 __all__ 機制來防止導入全局變量;或者爲全局變量加一個前置下劃線。
函數名
函數名應該爲小寫,必要時可用下劃線分隔單詞以增加可讀性。 mixedCase(混合大小寫)僅被允許用於兼容性考慮(如: threading.py)。
函數和方法的參數
實例方法第一個參數是 'self'。
類方法第一個參數是 'cls'。
如果函數的參數名與保留關鍵字衝突,通常在參數名後加一個下劃線。
方法名和實例變量
同函數命名規則。
非公開方法和實例變量增加一個前置下劃線。
爲避免與子類命名衝突,採用兩個前置下劃線來觸發重整。類Foo屬性名爲__a, 不能以 Foo.__a訪問。(執著的用戶還是可以通過Foo._Foo__a。) 通常雙前置下劃線僅被用來避免與基類的屬性發生命名衝突。
常量
常量通常在模塊級定義,由大寫字母用下劃線分隔組成。比如括MAX_OVERFLOW和TOTAL。
繼承設計
考慮類的方法和實例變量(統稱爲屬性)是否公開。如果有疑問,選擇不公開;把其改爲公開比把公開屬性改爲非公開要容易。
公開屬性可供所有人使用,並通常向後兼容。非公開屬性不給第三方使用、可變甚至被移除。
這裏不使用術語"private", Python中沒有屬性是真正私有的。
另一類屬性是子類API(在其他語言中通常稱爲 "protected")。 一些類被設計爲基類,可以擴展和修改。
謹記這些Python指南:
公開屬性應該沒有前導下劃線。
如果公開屬性名和保留關鍵字衝突,可以添加後置下劃線
簡單的公開數據屬性,最好只公開屬性名,沒有複雜的訪問/修改方法,python的Property提供了很好的封裝方法。 d.如果不希望子類使用的屬性,考慮用兩個前置下劃線(沒有後置下劃線)命名。
公共和內部接口
任何向後兼容的保證只適用於公共接口。
文檔化的接口通常是公共的,除非明說明是臨時的或爲內部接口、其他所有接口默認是內部的。
爲了更好地支持內省,模塊要在__all__屬性列出公共API。
內部接口要有前置下劃線。
如果命名空間(包、模塊或類)是內部的,裏面的接口也是內部的。
導入名稱應視爲實現細節。其他模塊不能間接訪名字,除非在模塊的API文檔中明確記載,如os.path中或包的__init__暴露了子模塊。
編程建議
考慮多種Python實現(PyPy, Jython, IronPython,Pyrex, Psyco, 等等)。
例如,CPython對a+=b或a=a+b等語句有高效的實現,但在Jython中運行很慢,儘量改用.join()。
None比較用'is'或'is not',不要用等號。
注意"if x is not None" 與"if x" 的區別。
用"is not"代替"not ... is"。前者的可讀性更好。
# Yes if foo is not None # No if not foo is None
使用基於類的異常。
比較排序操作最好是實現所有六個操作,而不是代碼中實現比較邏輯。functools.total_ordering()裝飾符可以生成缺失的比較方法。
__eq__,__ne__,__lt__,__lt__,__gt__,____)
PEP207 比較標準表明反射規則由Python完成。因此解釋器可能會交換參數的位置,比如替換y > x爲x < y,所以有必要實現這5種方法。
使用函數定義def代替lambda賦值給標識符:
# Yes def f(x): return 2*x # No f = lambda x: 2*x
前者更適合回調和字符串表示。
異常類繼承自Exception,而不是BaseException。
源於異常,而不是BaseException例外。從BaseException直接繼承的例外情況追趕他們幾乎總是錯誤的事情做保留。
要設計基於層次的異常,捕捉到需要的異常,而不是異常引發的位置。能回答:“出了什麼問題?”,而不是僅僅指出“問題發生”(更多參考:PEP3151 重構OS和IO異常層次)
適當使用異常鏈。在Python3中"raise X from Y"明確表示更換且保留了原來的traceback。
替換內部異常(在Python2: "raise X"或"raise X from None")時,確保相關細節轉移到新的異常(如轉換KeyError爲AttributeError保存屬性名,或在新的異常中嵌入原始異常)。
Python2中用" raise ValueError('message')"代替"raise ValueError, 'message'"
後者不兼容Python3語法。前者續行方便。
捕獲異常時儘量指明具體異常,而不是空"except:"子句。比如:
# Yes try: import platform_specific_module except ImportError: platform_specific_module = None
空"except:"子句(相當於except Exception)會捕捉SystemExit和KeyboardInterrupt異常,難以用Control-C中斷程序,並可掩蓋其他問題。如果 你捕捉信號錯誤之外所有的異常,使用"except Exception"。
空"except:"子句適用的情況兩種情況:
a, 打印出或記錄了traceback,至少讓用戶將知道已發生錯誤。
b, 代碼需要做一些清理工作,並用 raise轉發了異常。這樣try...finally可以捕捉到它。
Python 2.6以後建議用as顯示綁定異常名:
# Yes try: process_data() except Exception as exc: raise DataProcessingFailedError(str(exc))
這樣才能兼容Python3語法並避免歧義。
捕捉操作系統錯誤時,建議使用Python 3.3引入顯式異常層次,支持內省errno值。
此外所有try/except子句的代碼要儘可的少,以免屏蔽其他的錯誤。
# Yes try: value = collection[key] except KeyError: return key_not_found(key) else: return handle_value(value) # No try: # 太泛了! return handle_value(collection[key]) except KeyError: # 會捕捉到handle_value()中的KeyError return key_not_found(key)
本地資源建議使用with語句,以確保即時清理。當然try / finally語句也是可以接受的。
上下文管理器在做獲取和釋放資源之外的事情時,應通過獨立的函數或方法。例如:
# Yes with conn.begin_transaction(): do_stuff_in_transaction(conn) # No with conn: do_stuff_in_transaction(conn)
後者指明enter和exit方法。
函數或者方法在沒有返回時要明確返回None。
使用字符串方法而不是string模塊。
python 2.0以後字符串方法總是更快,且Unicode字符串相同的API。
使用使用 .startswith()和.endswith()代替字符串切片來檢查前綴和後綴。and
startswith()和endswith更簡潔,利於減少錯誤。例如:
# Yes if foo.startswith('bar'): # No if foo[:3] == 'bar':
使用isinstance()代替對象類型的比較:
# Yes if isinstance(obj, int): # No if type(obj) is type(1):
檢查是否是字符串時,注意Python 2中str和unicode有公共的基類:
if isinstance(obj, basestring): 在 Python 2.2 中,types 模塊爲此定義了 StringTypes 類型,例如:
# Yes if isinstance(obj, basestring):
Python3中Unicode和basestring的不再存在(只有str)和字節對象不再是字符串(是整數序列)
對序列(字符串、列表 、元組), 空序列爲false:
# Yes if not seq: pass if seq: pass # No if len(seq): pass if not len(seq): pass
字符串後面不要有大量拖尾空格。
不要用 == 進行布爾比較
# Yes if greeting:: pass # No if greeting == True pass if greeting is True: # Worse pass
本文原文連接:http://my.oschina.net/u/1433482/blog/464444?p=1