出自http://wiki.woodpecker.org.cn/moin/PythonCodingRule
1. Python Coding Rule
--- hoxide 初譯 dreamingk 校對發布 040724
--- xyb 重新排版 040915
--- ZoomQuiet MoinMoin 美化 050610
用Python進行開發時的編碼風格約定 原文:PEP 008《Style Guide for Python Code》 本地下載: PythonCodingRule.pdf
1.1. 介紹
* 這篇文檔所給出的編碼約定適用於在主要的Python發布版本中組成標準庫的Python 代碼.請查閱相關的關於在Python的C實現中C代碼風格指南的描述. 這篇文檔改編自Guido最初的《Python風格指南》一文. 並從《Barry's style guide》中添加了部分內容. 在有衝突的地方,Guide的風格規則應該是符合本PEP的意圖 (譯註:就是當有衝突時,應以Guido風格為準) 這篇PEP也許仍然尚未完成(實際上,它可能永遠不會結束).
1.2. 一致性的建議
愚蠢得使用一致性是無知的妖怪(A Foolish Consistency is the Hobgoblin of Little Minds)
呆板的堅持一致性是傻的沒邊了!
-- Zoomq
* 在這篇風格指導中的一致性是重要的. 在一個項目內的一致性更重要. 在一個模塊或函數內的一致性最重要. 但最重要的是:知道何時會不一致 -- 有時只是沒有實施風格指導.當出現疑惑時,
o 運用你的最佳判斷.看看別的例子,然後決定怎樣看起來更好.並且要不恥下問!
* 打破一條既定規則的兩個好理由:
1. 當應用這個規則是將導致代碼可讀性下降,即便對某人來說,他已經習慣於按這條規則來閱讀代碼了.
2. 為了和周圍的代碼保持一致而打破規則(也許是歷史原因)
o -- 雖然這也是個清除其它混亂的好機會(真正的XP風格).
1.3. 代碼的布局
(Code lay-out)
1.3.1. 縮進
(Indentation)
* 使用Emacs的Python-mode的默認值:4個空格一個縮進層次. 對於確實古老的代碼,你不希望產生混亂,可以繼續使用8空格的製表符(8-space tabs). Emacs Python-mode自動發現文件中主要的縮進層次,依此設定縮進參數.
1.3.2. 製表符還是空格?
(Tabs or Spaces)
* 永遠不要混用製表符和空格. 最流行的Python縮進方式是僅使用空格, 其次是僅使用製表符.混合著製表符和空格縮進的代碼將被轉換成僅使用空格. (在Emacs中,選中整個緩衝區,按ESC-x去除製表符(untabify).) 調用python命令行解釋器時使用-t選項,可對代碼中不合法得混合製表符和空格發出警告(warnings). 使用-tt時警告(warnings)將變成錯誤(errors).這些選項是被高度推薦的. 對於新的項目,強烈推薦僅使用空格(spaces-only)而不是製表符. 許多編輯器擁有使之易於實現的功能.(在Emacs中,確認indent-tabs-mode是nil).
1.3.3. 行的最大長度
(Maximum Line Length)
* 周圍仍然有許多設備被限制在每行80字元;而且,窗口限制在80個字元 使將多個窗口並排放置成為可能.在這些設備上使用默認的摺疊(wrapping)方式看起來有點醜陋. 因此,請將所有行限制在最大79字元(Emacs準確得將行限制為長80字元), 對順序排放的大塊文本(文檔字元串或註釋),推薦將長度限制在72字元. 摺疊長行的首選方法是使用Pyhon支持的圓括弧,方括弧(brackets)和花括弧(braces)內的行延續. 如果需要,你可以在表達式周圍增加一對額外的圓括弧, 但是有時使用反斜杠看起來更好.確認恰當得縮進了延續的行. Emacs的Python-mode正確得完成了這些.一些例子:
Toggle line numbers
1 class Rectangle(Blob):
2
3 def __init__(self, width, height,
4 color='black', emphasis=None, highlight=0):
5 if width == 0 and height == 0 and \
color == 'red' and emphasis == 'strong' or \
highlight > 100:
6 raise ValueError, "sorry, you lose"
7 if width == 0 and height == 0 and (color == 'red' or
8 emphasis is None):
9 raise ValueError, "I don't think so"
10 Blob.__init__(self, width, height,
11 color, emphasis, highlight)
1.3.4. 空行
(Blank Lines)
* 用兩行空行分割頂層函數和類的定義,類內方法的定義用單個空行分割. 額外的空行可被用於(保守的(sparingly))分割相關函數組成的群(groups of related functions). 在一組相關的單句中間可以省略空行.(例如.一組啞元(a set of dummy implementations)). 當空行用於分割方法(method)的定義時,在'class'行和第一個方法定義之間也要有一個空行. 在函數中使用空行時,請謹慎的用於表示一個邏輯段落(indicate logical sections). Python接受contol-L(即^L)換頁符作為空格;Emacs(和一些列印工具) 視這個字元為頁面分割符,因此在你的文件中,可以用他們來為相關片段(sections)分頁.
1.3.5. 編碼
(Encodings)(PEP 263)
* Python核心發布中的代碼必須始終使用ASCII或Latin-1編碼(又名 ISO-8859-1). 使用ASCII的文件不必有解碼cookie(coding cookie). Latin-1僅當註釋或文檔字元串涉及作者名字需要Latin-1時才被使用; 另外使用\x轉義字元是在字元串中包含非ASCII(non-ASCII)數據的首選方法. 作為PEP 263實現代碼的測試套件的部分文件是個例外.
Python 2.4 以後內核支持 Unicode 了!
不論什麼情況使用 UTF-8 吧!這是王道!
--ZoomQuiet
1.4. 導入
(Imports)
* 通常應該在單獨的行中導入(Imports),例如:
No: import sys, os
Yes: import sys
import os
* 但是這樣也是可以的:
from types import StringType, ListType
* Imports 通常被放置在文件的頂部,僅在模塊註釋和文檔字元串之後,在模塊的全局變數和常量之前.Imports應該有順序地成組安放.
1. 標準庫的導入(Imports )
2. 相關的主包(major package)的導入(即,所有的email包在隨後導入)
3. 特定應用的導入(imports)
* 你應該在每組導入之間放置一個空行.
* 對於內部包的導入是不推薦使用相對導入的.對所有導入都要使用包的絕對路徑.
* 從一個包含類的模塊中導入類時,通常可以寫成這樣:
from MyClass import MyClass
from foo.bar.YourClass import YourClass
* 如果這樣寫導致了本地名字衝突,那麼就這樣寫
import MyClass
import foo.bar.YourClass
*
即使用"MyClass.MyClass"和"foo.bar.YourClass.YourClass"
1.5. 空格
(Whitespace in Expressions and Statements)
* Guido不喜歡在以下地方出現空格:
*
"spam( ham[ 1 ], { eggs: 2 } )". Always write this as "spam(ham[1], {eggs: 2})".
o
緊挨著圓括弧,方括弧和花括弧的,如:"spam( ham[ 1 ], { eggs: 2 } )".
要始終將它寫成"spam(ham[1], {eggs: 2})".
"if x == 4 : print x , y ; x , y = y , x". Always write this as "if x == 4: print x, y; x, y = y, x".
o 緊貼在逗號,分號或冒號前的,如:
"if x == 4 : print x , y ; x , y = y , x". 要始終將它寫成 "if x == 4: print x, y; x, y = y, x".
o
緊貼著函數調用的參數列表前開式括弧(open parenthesis )的,如"spam (1)".要始終將它寫成"spam(1)".
slicing, as in: "dict ['key'] = list [index]". Always write this as "dict['key'] = list[index]".
o 緊貼在索引或切片(slicing?下標?)開始的開式括弧前的,如:
"dict ['key'] = list [index]".要始終將它寫成"dict['key'] = list[index]".
o 在賦值(或其它)運算符周圍的用於和其它並排的一個以上的空格,如:
Toggle line numbers
1 x = 1
2 y = 2
3 long_variable = 3
* 要始終將它寫成
Toggle line numbers
1 x = 1
2 y = 2
3 long_variable = 3
* (不要對以上任意一條和他爭論 --- Guido 養成這樣的風格超過20年了.)
1.5.1. 其它建議
(Other Recommendations)
*
始終在這些二元運算符兩邊放置一個空格:賦值(=), 比較(==, <, >, !=, <>, <=,>=, in, not in, is, is not), 布爾運算 (and, or, not).
* 按你的看法在算術運算符周圍插入空格. 始終保持二元運算符兩邊空格的一致.
* 一些例子:
Toggle line numbers
1 i = i+1
2 submitted = submitted + 1
3 x = x*2 - 1
4 hypot2 = x*x + y*y
5 c = (a+b) * (a-b)
6 c = (a + b) * (a - b)
* 不要在用於指定關鍵字參數或默認參數值的'='號周圍使用空格,例如:
Toggle line numbers
1 def complex(real, imag=0.0):
2 return magic(r=real, i=imag)
* 不要將多條語句寫在同一行上.
No: if foo == 'blah': do_blah_thing()
Yes: if foo == 'blah':
do_blah_thing()
No: do_one(); do_two(); do_three()
Yes: do_one()
do_two()
do_three()
1.6. 註釋
(Comments)
* 同代碼不一致的註釋比沒註釋更差.當代碼修改時,始終優先更新註釋! 註釋應該是完整的句子. 如果註釋是一個短語或句子,首字母應該大寫, 除非他是一個以小寫字母開頭的標識符(永遠不要修改標識符的大小寫). 如果註釋很短,最好省略末尾的句號(period?結尾句末的停頓?也可以是逗號吧,) 註釋塊通常由一個或多個由完整句子構成的段落組成,每個句子應該以句號結尾. 你應該在句末,句號后使用兩個空格,以便使Emacs的斷行和填充工作協調一致 (譯按:應該說是使這兩種功能正常工作,". "給出了文檔結構的提示). 用英語書寫時,斷詞和空格是可用的. 非英語國家的Python程序員:請用英語書寫你的註釋,除非你120%的確信 這些代碼不會被不懂你的語言的人閱讀.
我就是堅持全部使用中文來註釋,真正要發布腳本工具時,再想E文的;
開發時每一瞬間都要用在思量中,堅決不用在E文語法,單詞的回憶中!
-- ZoomQUiet
*
約定使用統一的文檔化註釋格式有利於良好習慣和團隊建議!-- CodeCommentingRule
1.6.1. 註釋塊
(Block Comments)
* 註釋塊通常應用於跟隨著一些(或者全部)代碼並和這些代碼有著相同的縮進層次. 註釋塊中每行以'#'和一個空格開始(除非他是註釋內的縮進文本). 註釋塊內的段落以僅含單個'#'的行分割. 註釋塊上下方最好有一空行包圍(或上方兩行下方一行,對一個新函數定義段的註釋).
1.6.2. 行內註釋
(Inline Comments)
* (inline?內聯?翻成"行內"比較好吧)
o 一個行內註釋是和語句在同一行的註釋.行內註釋應該謹慎適用. 行內註釋應該至少用兩個空格和語句分開. 它們應該以'#'和單個空格開始.
x = x+1 # Increment x
* 如果語意是很明了的,那麼行內註釋是不必要的,事實上是應該被去掉的. 不要這樣寫:
x = x+1 # Increment x
x = x+1 # Compensate for border
* 但是有時,這樣是有益的:
x = x+1 # Compensate for border
1.7. 文檔化
(Documentation Strings)
* Conventions for writing good documentation strings (a.k.a. "docstrings") are immortalized in
PEP 257. 應該一直遵守編寫好的文檔字元串(又名"docstrings")的約定(?實在不知道怎麼譯)
Documentation Strings-- 文檔化字元 ;
為配合 pydoc;epydoc,Doxygen等等文檔化工具的使用,類似於MoinMoin 語法,約定一些字元,
以便自動提取轉化為有意義的文檔章節等等文章元素!
-- Zoomq
* 為所有公共模塊,函數,類和方法編寫文檔字元串.文檔字元串對非公開的方法不是必要的,但你應該有一個描述這個方法做什麼的註釋.這個註釋應該在"def"這行后.
*
PEP 257 描述了好的文檔字元串的約定.一定注意,多行文檔字元串結尾的""" 應該單獨成行,例如:
"""Return a foobang
Optional plotz says to frobnicate the bizbaz first.
"""
* 對單行的文檔字元串,結尾的"""在同一行也可以.
實際上Python 自個兒就使用文檔化編碼維護著所有內置對象的使用說明\
不信的話常試:
#python
>>> import time
>>> dir(time)
['__doc__', '__file__', '__name__', 'accept2dyear', 'altzone', 'asctime', 'clock', 'ctime', 'daylight', 'gmtime', 'localtime', 'mktime', 'sleep', 'strftime', 'strptime', 'struct_time', 'time', 'timezone', 'tzname', 'tzset']
>>> help(time.time)
Help on built-in function time in module time:
time(...)
time() -> floating point number
Return the current time in seconds since the Epoch.
Fractions of a second may be present if the system clock provides them.
1.8. 版本註記
(Version Bookkeeping) (我覺得叫"註記"更好)
* 如果你要將RCS或CVS的雜項(crud)包含在你的源文件中,按如下做.
Toggle line numbers
1 __version__ = "$Revision: 1.4 $"
2 # $Source: E:/cvsroot/python_doc/pep8.txt,v $
* 這個行應該包含在模塊的文檔字元串之後,所有代碼之前,上下用一個空行分割.
對於CVS的伺服器工作標記更應該在代碼段中明確出它的使用
如:在文檔的最開始的版權聲明后應加入如下版本標記:
# 文件:$id$
# 版本: $Revision$
這樣的標記在提交給配置管理伺服器后,會自動適配成為相應的字元串,如:
# 文件:$Id: ussp.py,v 1.22 2004/07/21 04:47:41 hd Exp $
# 版本: $Revision: 1.4 $
----HD
1.9. 命名約定
(Naming Conventions)
* Python庫的命名約定有點混亂,所以我們將永遠不能使之變得完全一致--- 不過還是有公認的命名規範的. 新的模塊和包(包括第三方的框架)必須符合這些標準,但對已有的庫存在不同風格的, 保持內部的一致性是首選的.
1.9.1. 描述:命名風格
(Descriptive: Naming Styles)
* 有許多不同的命名風格.以下的有助於辨認正在使用的命名風格,獨立於它們的作用. 以下的命名風格是眾所周知的:
* b (單個小寫字母)
* B (單個大寫字母)
* 小寫串 如:getname
* 帶下划的小寫串 如:_getname
* 大寫串 如:GETNAME
* 帶下划的大寫串 如:_GETNAME
*
CapitalizedWords(首字母大寫單詞串) (或 CapWords, CamelCase -- 這樣命名是由於它的字母錯落有致的樣子而來的.
o
這有時也被當作StudlyCaps. 如:GetName
* mixedCase (混合大小寫串)(與首字母大寫串不同之處在於第一個字元是小寫如:getName)
* Capitalized_Words_With_Underscores(帶下劃線的首字母大寫串) (醜陋!) 還有一種使用特別前綴的風格,用於將相關的名字分成組.這在Python中不常用, 但是出於完整性要提一下.例如,os.stat()函數返回一個tuple, 他的元素傳統上有象st_mode, st_size, st_mtime等等這樣的名字. X11庫的所有公開函數以X開頭.(在Python中,這個風格通常認為是不必要的, 因為屬性和方法名以對象作前綴,而函數名以模塊名作前綴.) 另外,以下用下劃線作前導或結尾的特殊形式是被公認的(這些通常可以和任何習慣組合(使用?)):
* _single_leading_underscore(以一個下劃線作前導): 弱的"內部使用(internal use)"標誌.
o (例如,"from M import *"不會導入以下劃線開頭的對象).
* single_trailing_underscore_(以一個下劃線結尾): 用於避免與Python關鍵詞的衝突,例如.
o
"Tkinter.Toplevel(master, class_='ClassName')".
*
__double_leading_underscore(雙下劃線): 從Python 1.4起為類私有名.
*
__double_leading_and_trailing_underscore__: 特殊的(magic) 對象或屬性,存在於用戶控制的(user-controlled)名字空間, 例如:__init__, __import__ 或 __file__. 有時它們被用戶定義, 用於觸發某個特殊行為(magic behavior)(例如:運算符重載); 有時被構造器(infrastructure)插入,以便自己使用或為了調試. 因此,在未來的版本中,構造器(鬆散得定義為Python解釋器和標準庫) 可能打算建立自己的魔法屬性列表,用戶代碼通常應該限制將這種約定作為己用. 欲成為構造器的一部分的用戶代碼可以在下滑線中結合使用短前綴,例如. __bobo_magic_attr__.
1.9.2. 說明:命名約定
(Prescriptive: Naming Conventions)
1.9.2.1. 應避免的名字
(Names to Avoid)
* 永遠不要用字元`l'(小寫字母el(就是讀音,下同)),
O'(大寫字母oh),或I'(大寫字母eye)作為單字元的變數名. 在某些字體中,這些字元不能與數字1和0分開.當想要使用'l'時,用'L'代替它.
1.9.2.2. 模塊名
(Module Names)
* 模塊應該是不含下劃線的,簡短的,小寫的名字. 因為模塊名被映射到文件名, 有些文件系統大小寫不敏感並且截短長名字, 模塊名被選為相當短是重要的---這在Unix上不是問題, 但當代碼傳到Mac 或Windows上就可能是個問題了. 當一個用C或C++寫的擴展模塊有一個伴隨的Python模塊,這個Python模塊提供了
o 一個更高層(例如,更面向對象)的介面時,C/C++模塊有一個前導下劃線(如:_socket)
Python包應該是不含下劃線的,簡短的,全小寫的名字.
1.9.2.3. 類名
(Class Names)
*
幾乎沒有例外,類名總是使用首字母大寫單詞串(CapWords)的約定.
1.9.2.4. 異常名
(Exception Names)
* 如果模塊對所有情況定義了單個異常,它通常被叫做"error"或"Error". 似乎內建(擴展)的模塊使用"error"(例如:os.error), 而Python模塊通常用"Error" (例如: xdrlib.Error).
趨勢似乎是傾向使用CapWords異常名.
1.9.2.5. 全局變數名
(Global Variable Names)
* (讓我們希望這些變數打算只被用於模塊內部) 這些約定與那些用於函數的約定差不多.被設計可以通過"from M import *"來使用的
o 那些模塊,應該在那些不想被導入的全局變數(還有內部函數和類)前加一個下劃線).
1.9.2.6. 函數名
(Function Names)
* 函數名應該為小寫,可能用下劃線風格單詞以增加可讀性. mixedCase僅被允許用於這種風格已經佔優勢的上下文(如: threading.py) 以便保持向後兼容.
1.9.2.7. 方法名和實例變數
(Method Names and Instance Variables)
* 這段大體上和函數相同:通常使用小寫單詞,必要時用下劃線分隔增加可讀性. 使用一個前導下劃線僅用於不打算作為類的公共介面的內部方法和實例變數. Python不強制要求這樣; 它取決於程序員是否遵守這個約定. 使用兩個前導下劃線以表示類私有的名字. Python將這些名字和類名連接在一起:
如果類Foo有一個屬性名為 __a, 它不能以Foo.__a訪問. (執著的用戶(An insistent user)還是可以通過Foo._Foo__a得到訪問權.) 通常,雙前導下劃線應該只用來避免與類(為可以子類化所設計)中的屬性發生名字衝突.
1.9.2.8. 繼承的設計
(Designing for inheritance)
* 始終要確定一個類中的方法和實例變數是否要被公開. 通常,永遠不要將數據變數公開,除非你實現的本質上只是記錄. 人們總是更喜歡給類提供一個函數的介面作為替換 (Python 2.2 的一些開發者在這點上做得非常漂亮). 同樣,確定你的屬性是否應為私有的.私有與非公有的區別在於: 前者永遠不會被用在一個派生類中,而後者可能會. 是的,你應該在大腦中就用繼承設計好了你的類. 私有屬性必須有兩個前導下劃線,無後置下劃線. 非公有屬性必須有一個前導下劃線,無後置下劃線. 公共屬性沒有前導和後置下劃線,除非它們與保留字衝突, 在此情況下,單個後置下劃線比前置或混亂的拼寫要好, 例如:class_優於klass. 最後一點有些爭議; 如果相比class_你更喜歡klass,那麼這只是一致性問題.
1.10. 設計建議
(Programming Recommendations)
* 同象None之類的單值進行比較,應該永遠用:'is'或'is not'來做. 當你本意是"if x is not None"時,對寫成"if x"要小心 -- 例如當你測試一個默認為None的變數或參數是否被設置為其它值時. 這個其它值可能是一個在布爾上下文中為假的值!
* 基於類的異常總是好過基於字元串的異常. 模塊和包應該定義它們自己的域內特定的基異常類(base exception class), 基類應該是內建的Exception類的子類. 還始終包含一個類的文檔字元串.例如:
Toggle line numbers
1 class MessageError(Exception):
2 """Base class for errors in the email package."""
* 使用字元串方法(methods)代替字元串模塊,除非必須向後兼容Python 2.0以前的版本. 字元串方法總是非常快,而且和unicode字元串共用同樣的API(應用程序介面)
* 在檢查前綴或後綴時避免對字元串進行切片. 用startswith()和endswith()代替, 因為它們是明確的並且錯誤更少. 例如:
No: if foo[:3] == 'bar':
Yes: if foo.startswith('bar'):
* 例外是如果你的代碼必須工作在Python 1.5.2 (但是我們希望它不會發生!).
* 對象類型的比較應該始終用isinstance()代替直接比較類型.例如:
No: if type(obj) is type(1):
Yes: if isinstance(obj, int):
* 檢查一個對象是否是字元串時,緊記它也可能是unicode字元串! 在Python 2.3, str和unicode有公共的基類,basestring,所以你可以這樣做:
Toggle line numbers
1 if isinstance(obj, basestring):
*
在Python 2.2 類型模塊為此定義了StringTypes類型, 例如:
Toggle line numbers
1 from types import StringTypes
2 if isinstance(obj, StringTypes):
* 在Python 2.0和2.1,你應該這樣做:
Toggle line numbers
1 from types import StringType, UnicodeType
2 if isinstance(obj, StringType) or \
isinstance(obj, UnicodeType) :
* 對序列,(字元串(strings),列表(lists),元組(tuples)), 使用空列表是false這個事實,因此"if not seq"或"if seq"比 "if len(seq)"或"if not len(seq)"好.
* 書寫字元串文字時不要依賴於有意義的後置空格. 這種後置空格在視覺上是不可辨別的,並且有些編輯器(特別是近來,reindent.py) 會將它們修整掉.
* 不要用 == 來比較布爾型的值以確定是True或False(布爾型是Pythn 2.3中新增的)
No: if greeting == True:
Yes: if greeting:
No: if greeting == True:
Yes: if greeting: