背景
Python 是 Google主要的腳本語言。這本風(fēng)格指南主要包含的是針對python的編程準(zhǔn)則。
為幫助讀者能夠?qū)⒋a準(zhǔn)確格式化,我們提供了針對?Vim的配置文件?。對于Emacs用戶,保持默認(rèn)設(shè)置即可。
Python語言規(guī)范
Lint
對你的代碼運(yùn)行pylint
定義:pylint是一個在Python源代碼中查找bug的工具. 對于C和C++這樣的不那么動態(tài)的(譯者注: 原文是less dynamic)語言, 這些bug通常由編譯器來捕獲. 由于Python的動態(tài)特性, 有些警告可能不對. 不過偽告警應(yīng)該很少.優(yōu)點(diǎn):可以捕獲容易忽視的錯誤, 例如輸入錯誤, 使用未賦值的變量等.缺點(diǎn):pylint不完美. 要利用其優(yōu)勢, 我們有時侯需要: a) 圍繞著它來寫代碼 b) 抑制其告警 c) 改進(jìn)它, 或者d) 忽略它.結(jié)論:確保對你的代碼運(yùn)行pylint.抑制不準(zhǔn)確的警告,以便能夠?qū)⑵渌姹┞冻鰜?。你可以通過設(shè)置一個行注釋來抑制告警. 例如:
dict = 'something awful'? # Bad Idea... pylint: disable=redefined-builtin
pylint警告是以一個數(shù)字編號(如C0112)和一個符號名(如empty-docstring)來標(biāo)識的. 在編寫新代碼或更新已有代碼時對告警進(jìn)行醫(yī)治, 推薦使用符號名來標(biāo)識.
如果警告的符號名不夠見名知意,那么請對其增加一個詳細(xì)解釋。
采用這種抑制方式的好處是我們可以輕松查找抑制并回顧它們.
你可以使用命令pylint?--list-msgs來獲取pylint告警列表. 你可以使用命令pylint?--help-msg=C6409, 以獲取關(guān)于特定消息的更多信息.
相比較于之前使用的pylint:?disable-msg, 本文推薦使用pylint:?disable.
要抑制”參數(shù)未使用”告警, 你可以用”_”作為參數(shù)標(biāo)識符, 或者在參數(shù)名前加”unused_”. 遇到不能改變參數(shù)名的情況, 你可以通過在函數(shù)開頭”提到”它們來消除告警. 例如:
def foo(a, unused_b, unused_c, d=None, e=None):
? ? _ = d, e
? ? return a
導(dǎo)入
僅對包和模塊使用導(dǎo)入
定義:模塊間共享代碼的重用機(jī)制.優(yōu)點(diǎn):命名空間管理約定十分簡單. 每個標(biāo)識符的源都用一種一致的方式指示. x.Obj表示Obj對象定義在模塊x中.缺點(diǎn):模塊名仍可能沖突. 有些模塊名太長, 不太方便.結(jié)論:使用import?x來導(dǎo)入包和模塊.使用from?x?import?y, 其中x是包前綴, y是不帶前綴的模塊名.使用from?x?import?y?as?z, 如果兩個要導(dǎo)入的模塊都叫做y或者y太長了.例如, 模塊sound.effects.echo可以用如下方式導(dǎo)入:
from sound.effects import echo
...
echo.EchoFilter(input, output, delay=0.7, atten=4)
導(dǎo)入時不要使用相對名稱. 即使模塊在同一個包中, 也要使用完整包名. 這能幫助你避免無意間導(dǎo)入一個包兩次.
本人對于Python學(xué)習(xí)創(chuàng)建了一個小小的學(xué)習(xí)圈子,為各位提供了一個平臺,大家一起來討論學(xué)習(xí)Python。歡迎各位到來Python學(xué)習(xí)群:960410445一起討論視頻分享學(xué)習(xí)。Python是未來的發(fā)展方向,正在挑戰(zhàn)我們的分析能力及對世界的認(rèn)知方式,因此,我們與時俱進(jìn),迎接變化,并不斷的成長,掌握Python核心技術(shù),才是掌握真正的價值所在。
包
使用模塊的全路徑名來導(dǎo)入每個模塊
優(yōu)點(diǎn):避免模塊名沖突. 查找包更容易.缺點(diǎn):部署代碼變難, 因?yàn)槟惚仨殢?fù)制包層次.結(jié)論:所有的新代碼都應(yīng)該用完整包名來導(dǎo)入每個模塊.應(yīng)該像下面這樣導(dǎo)入:
# Reference in code with complete name.
import sound.effects.echo
# Reference in code with just module name (preferred).
from sound.effects import echo
異常
允許使用異常, 但必須小心
定義:異常是一種跳出代碼塊的正??刂屏鱽硖幚礤e誤或者其它異常條件的方式.優(yōu)點(diǎn):正常操作代碼的控制流不會和錯誤處理代碼混在一起. 當(dāng)某種條件發(fā)生時, 它也允許控制流跳過多個框架. 例如, 一步跳出N個嵌套的函數(shù), 而不必繼續(xù)執(zhí)行錯誤的代碼.缺點(diǎn):可能會導(dǎo)致讓人困惑的控制流. 調(diào)用庫時容易錯過錯誤情況.結(jié)論:異常必須遵守特定條件:
像這樣觸發(fā)異常:raise?MyException("Error?message")或者raise?MyException. 不要使用兩個參數(shù)的形式(raise?MyException,?"Error?message")或者過時的字符串異常(raise?"Error?message").
模塊或包應(yīng)該定義自己的特定域的異常基類, 這個基類應(yīng)該從內(nèi)建的Exception類繼承. 模塊的異?;悜?yīng)該叫做”Error”.
class Error(Exception):
? ? pass
永遠(yuǎn)不要使用except:語句來捕獲所有異常, 也不要捕獲Exception或者StandardError, 除非你打算重新觸發(fā)該異常, 或者你已經(jīng)在當(dāng)前線程的最外層(記得還是要打印一條錯誤消息). 在異常這方面, Python非常寬容,except:真的會捕獲包括Python語法錯誤在內(nèi)的任何錯誤. 使用except:很容易隱藏真正的bug.
盡量減少try/except塊中的代碼量. try塊的體積越大, 期望之外的異常就越容易被觸發(fā). 這種情況下, try/except塊將隱藏真正的錯誤.
使用finally子句來執(zhí)行那些無論try塊中有沒有異常都應(yīng)該被執(zhí)行的代碼. 這對于清理資源常常很有用, 例如關(guān)閉文件.
當(dāng)捕獲異常時, 使用as而不要用逗號. 例如
try:
? ? raise Error
except Error as error:
? ? pass
全局變量
避免全局變量
定義:定義在模塊級的變量.優(yōu)點(diǎn):偶爾有用.缺點(diǎn):導(dǎo)入時可能改變模塊行為, 因?yàn)閷?dǎo)入模塊時會對模塊級變量賦值.結(jié)論:避免使用全局變量, 用類變量來代替. 但也有一些例外:
腳本的默認(rèn)選項(xiàng).
模塊級常量. 例如: PI = 3.14159. 常量應(yīng)該全大寫, 用下劃線連接.
有時候用全局變量來緩存值或者作為函數(shù)返回值很有用.
如果需要, 全局變量應(yīng)該僅在模塊內(nèi)部可用, 并通過模塊級的公共函數(shù)來訪問.
嵌套/局部/內(nèi)部類或函數(shù)
鼓勵使用嵌套/本地/內(nèi)部類或函數(shù)
定義:類可以定義在方法, 函數(shù)或者類中. 函數(shù)可以定義在方法或函數(shù)中. 封閉區(qū)間中定義的變量對嵌套函數(shù)是只讀的.優(yōu)點(diǎn):允許定義僅用于有效范圍的工具類和函數(shù).缺點(diǎn):嵌套類或局部類的實(shí)例不能序列化(pickled).結(jié)論:推薦使用.
列表推導(dǎo)(List Comprehensions)
可以在簡單情況下使用
定義:列表推導(dǎo)(list comprehensions)與生成器表達(dá)式(generator expression)提供了一種簡潔高效的方式來創(chuàng)建列表和迭代器, 而不必借助map(), filter(), 或者lambda.優(yōu)點(diǎn):簡單的列表推導(dǎo)可以比其它的列表創(chuàng)建方法更加清晰簡單. 生成器表達(dá)式可以十分高效, 因?yàn)樗鼈儽苊饬藙?chuàng)建整個列表.缺點(diǎn):復(fù)雜的列表推導(dǎo)或者生成器表達(dá)式可能難以閱讀.結(jié)論:適用于簡單情況. 每個部分應(yīng)該單獨(dú)置于一行: 映射表達(dá)式, for語句, 過濾器表達(dá)式. 禁止多重for語句或過濾器表達(dá)式. 復(fù)雜情況下還是使用循環(huán).
Yes:
? result = []
? for x in range(10):
? ? ? for y in range(5):
? ? ? ? ? if x * y > 10:
? ? ? ? ? ? ? result.append((x, y))
? for x in xrange(5):
? ? ? for y in xrange(5):
? ? ? ? ? if x != y:
? ? ? ? ? ? ? for z in xrange(5):
? ? ? ? ? ? ? ? ? if y != z:
? ? ? ? ? ? ? ? ? ? ? yield (x, y, z)
? return ((x, complicated_transform(x))
? ? ? ? ? for x in long_generator_function(parameter)
? ? ? ? ? if x is not None)
? squares = [x * x for x in range(10)]
? eat(jelly_bean for jelly_bean in jelly_beans
? ? ? if jelly_bean.color == 'black')
No:
? result = [(x, y) for x in range(10) for y in range(5) if x * y > 10]
? return ((x, y, z)
? ? ? ? ? for x in xrange(5)
? ? ? ? ? for y in xrange(5)
? ? ? ? ? if x != y
? ? ? ? ? for z in xrange(5)
? ? ? ? ? if y != z)
默認(rèn)迭代器和操作符
如果類型支持, 就使用默認(rèn)迭代器和操作符. 比如列表, 字典及文件等.
定義:容器類型, 像字典和列表, 定義了默認(rèn)的迭代器和關(guān)系測試操作符(in和not in)優(yōu)點(diǎn):默認(rèn)操作符和迭代器簡單高效, 它們直接表達(dá)了操作, 沒有額外的方法調(diào)用. 使用默認(rèn)操作符的函數(shù)是通用的. 它可以用于支持該操作的任何類型.缺點(diǎn):你沒法通過閱讀方法名來區(qū)分對象的類型(例如, has_key()意味著字典). 不過這也是優(yōu)點(diǎn).結(jié)論:如果類型支持, 就使用默認(rèn)迭代器和操作符, 例如列表, 字典和文件. 內(nèi)建類型也定義了迭代器方法. 優(yōu)先考慮這些方法, 而不是那些返回列表的方法. 當(dāng)然,這樣遍歷容器時,你將不能修改容器.
Yes:? for key in adict: ...
? ? ? if key not in adict: ...
? ? ? if obj in alist: ...
? ? ? for line in afile: ...
? ? ? for k, v in dict.iteritems(): ...
No:? for key in adict.keys(): ...
? ? ? if not adict.has_key(key): ...
? ? ? for line in afile.readlines(): ...
生成器
按需使用生成器.
定義:所謂生成器函數(shù), 就是每當(dāng)它執(zhí)行一次生成(yield)語句, 它就返回一個迭代器, 這個迭代器生成一個值. 生成值后, 生成器函數(shù)的運(yùn)行狀態(tài)將被掛起, 直到下一次生成.優(yōu)點(diǎn):簡化代碼, 因?yàn)槊看握{(diào)用時, 局部變量和控制流的狀態(tài)都會被保存. 比起一次創(chuàng)建一系列值的函數(shù), 生成器使用的內(nèi)存更少.缺點(diǎn):沒有.結(jié)論:鼓勵使用. 注意在生成器函數(shù)的文檔字符串中使用”Yields:”而不是”Returns:”.(譯者注: 參看注釋)
Lambda函數(shù)
適用于單行函數(shù)
定義:與語句相反, lambda在一個表達(dá)式中定義匿名函數(shù). 常用于為map()和filter()之類的高階函數(shù)定義回調(diào)函數(shù)或者操作符.優(yōu)點(diǎn):方便.缺點(diǎn):比本地函數(shù)更難閱讀和調(diào)試. 沒有函數(shù)名意味著堆棧跟蹤更難理解. 由于lambda函數(shù)通常只包含一個表達(dá)式, 因此其表達(dá)能力有限.結(jié)論:適用于單行函數(shù). 如果代碼超過60-80個字符, 最好還是定義成常規(guī)(嵌套)函數(shù).對于常見的操作符,例如乘法操作符,使用operator模塊中的函數(shù)以代替lambda函數(shù). 例如, 推薦使用operator.mul, 而不是lambda?x,?y:?x?*?y.
條件表達(dá)式
適用于單行函數(shù)
定義:條件表達(dá)式是對于if語句的一種更為簡短的句法規(guī)則. 例如:x?=?1?if?cond?else?2.優(yōu)點(diǎn):比if語句更加簡短和方便.缺點(diǎn):比if語句難于閱讀. 如果表達(dá)式很長, 難于定位條件.結(jié)論:適用于單行函數(shù). 在其他情況下,推薦使用完整的if語句.
默認(rèn)參數(shù)值
適用于大部分情況.
定義:你可以在函數(shù)參數(shù)列表的最后指定變量的值, 例如,def?foo(a,?b?=?0):. 如果調(diào)用foo時只帶一個參數(shù), 則b被設(shè)為0. 如果帶兩個參數(shù), 則b的值等于第二個參數(shù).優(yōu)點(diǎn):你經(jīng)常會碰到一些使用大量默認(rèn)值的函數(shù), 但偶爾(比較少見)你想要覆蓋這些默認(rèn)值. 默認(rèn)參數(shù)值提供了一種簡單的方法來完成這件事, 你不需要為這些罕見的例外定義大量函數(shù). 同時, Python也不支持重載方法和函數(shù), 默認(rèn)參數(shù)是一種”仿造”重載行為的簡單方式.缺點(diǎn):默認(rèn)參數(shù)只在模塊加載時求值一次. 如果參數(shù)是列表或字典之類的可變類型, 這可能會導(dǎo)致問題. 如果函數(shù)修改了對象(例如向列表追加項(xiàng)), 默認(rèn)值就被修改了.結(jié)論:鼓勵使用, 不過有如下注意事項(xiàng):不要在函數(shù)或方法定義中使用可變對象作為默認(rèn)值.
Yes: def foo(a, b=None):
? ? ? ? if b is None:
? ? ? ? ? ? b = []
No:? def foo(a, b=[]):
? ? ? ? ...
No:? def foo(a, b=time.time()):? # The time the module was loaded???
? ? ? ? ...
No:? def foo(a, b=FLAGS.my_thing):? # sys.argv has not yet been parsed...
? ? ? ? ...
屬性(properties)
訪問和設(shè)置數(shù)據(jù)成員時, 你通常會使用簡單, 輕量級的訪問和設(shè)置函數(shù). 建議用屬性(properties)來代替它們.
定義:一種用于包裝方法調(diào)用的方式. 當(dāng)運(yùn)算量不大, 它是獲取和設(shè)置屬性(attribute)的標(biāo)準(zhǔn)方式.優(yōu)點(diǎn):通過消除簡單的屬性(attribute)訪問時顯式的get和set方法調(diào)用, 可讀性提高了. 允許懶惰的計(jì)算. 用Pythonic的方式來維護(hù)類的接口. 就性能而言, 當(dāng)直接訪問變量是合理的, 添加訪問方法就顯得瑣碎而無意義. 使用屬性(properties)可以繞過這個問題. 將來也可以在不破壞接口的情況下將訪問方法加上.缺點(diǎn):屬性(properties)是在get和set方法聲明后指定, 這需要使用者在接下來的代碼中注意: set和get是用于屬性(properties)的(除了用@property裝飾器創(chuàng)建的只讀屬性). 必須繼承自object類. 可能隱藏比如操作符重載之類的副作用. 繼承時可能會讓人困惑.結(jié)論:你通常習(xí)慣于使用訪問或設(shè)置方法來訪問或設(shè)置數(shù)據(jù), 它們簡單而輕量. 不過我們建議你在新的代碼中使用屬性. 只讀屬性應(yīng)該用@property裝飾器來創(chuàng)建.如果子類沒有覆蓋屬性, 那么屬性的繼承可能看上去不明顯. 因此使用者必須確保訪問方法間接被調(diào)用, 以保證子類中的重載方法被屬性調(diào)用(使用模板方法設(shè)計(jì)模式).
Yes: import math
? ? class Square(object):
? ? ? ? """A square with two properties: a writable area and a read-only perimeter.
? ? ? ? To use:
? ? ? ? >>> sq = Square(3)
? ? ? ? >>> sq.area
? ? ? ? 9
? ? ? ? >>> sq.perimeter
? ? ? ? 12
? ? ? ? >>> sq.area = 16
? ? ? ? >>> sq.side
? ? ? ? 4
? ? ? ? >>> sq.perimeter
? ? ? ? 16
? ? ? ? """
? ? ? ? def __init__(self, side):
? ? ? ? ? ? self.side = side
? ? ? ? def __get_area(self):
? ? ? ? ? ? """Calculates the 'area' property."""
? ? ? ? ? ? return self.side ** 2
? ? ? ? def ___get_area(self):
? ? ? ? ? ? """Indirect accessor for 'area' property."""
? ? ? ? ? ? return self.__get_area()
? ? ? ? def __set_area(self, area):
? ? ? ? ? ? """Sets the 'area' property."""
? ? ? ? ? ? self.side = math.sqrt(area)
? ? ? ? def ___set_area(self, area):
? ? ? ? ? ? """Indirect setter for 'area' property."""
? ? ? ? ? ? self._SetArea(area)
? ? ? ? area = property(___get_area, ___set_area,
? ? ? ? ? ? ? ? ? ? ? ? doc="""Gets or sets the area of the square.""")
? ? ? ? @property
? ? ? ? def perimeter(self):
? ? ? ? ? ? return self.side * 4
(譯者注: 老實(shí)說, 我覺得這段示例代碼很不恰當(dāng), 有必要這么蛋疼嗎?)
True/False的求值
盡可能使用隱式false
定義:Python在布爾上下文中會將某些值求值為false. 按簡單的直覺來講, 就是所有的”空”值都被認(rèn)為是false. 因此0, None, [], {}, “” 都被認(rèn)為是false.優(yōu)點(diǎn):使用Python布爾值的條件語句更易讀也更不易犯錯. 大部分情況下, 也更快.缺點(diǎn):對C/C++開發(fā)人員來說, 可能看起來有點(diǎn)怪.結(jié)論:盡可能使用隱式的false, 例如: 使用if?foo:而不是if?foo?!=?[]:. 不過還是有一些注意事項(xiàng)需要你銘記在心:
永遠(yuǎn)不要用==或者!=來比較單件, 比如None. 使用is或者is not.
注意: 當(dāng)你寫下if?x:時, 你其實(shí)表示的是if?x?is?not?None. 例如: 當(dāng)你要測試一個默認(rèn)值是None的變量或參數(shù)是否被設(shè)為其它值. 這個值在布爾語義下可能是false!
永遠(yuǎn)不要用==將一個布爾量與false相比較. 使用if?not?x:代替. 如果你需要區(qū)分false和None, 你應(yīng)該用像if?not?x?and?x?is?not?None:這樣的語句.
對于序列(字符串, 列表, 元組), 要注意空序列是false. 因此if?not?seq:或者if?seq:比if?len(seq):或if?not?len(seq):要更好.
處理整數(shù)時, 使用隱式false可能會得不償失(即不小心將None當(dāng)做0來處理). 你可以將一個已知是整型(且不是len()的返回結(jié)果)的值與0比較.
Yes: if not users:
? ? ? ? print 'no users'
? ? if foo == 0:
? ? ? ? self.handle_zero()
? ? if i % 10 == 0:
? ? ? ? self.handle_multiple_of_ten()
No:? if len(users) == 0:
? ? ? ? print 'no users'
? ? if foo is not None and not foo:
? ? ? ? self.handle_zero()
? ? if not i % 10:
? ? ? ? self.handle_multiple_of_ten()
注意‘0’(字符串)會被當(dāng)做true.
過時的語言特性
盡可能使用字符串方法取代字符串模塊. 使用函數(shù)調(diào)用語法取代apply(). 使用列表推導(dǎo), for循環(huán)取代filter(), map()以及reduce().
定義:當(dāng)前版本的Python提供了大家通常更喜歡的替代品.結(jié)論:我們不使用不支持這些特性的Python版本, 所以沒理由不用新的方式.
Yes: words = foo.split(':')
? ? [x[1] for x in my_list if x[2] == 5]
? ? map(math.sqrt, data)? ? # Ok. No inlined lambda expression.
? ? fn(*args, **kwargs)
No:? words = string.split(foo, ':')
? ? map(lambda x: x[1], filter(lambda x: x[2] == 5, my_list))
? ? apply(fn, args, kwargs)
詞法作用域(Lexical Scoping)
推薦使用
定義:嵌套的Python函數(shù)可以引用外層函數(shù)中定義的變量, 但是不能夠?qū)λ鼈冑x值. 變量綁定的解析是使用詞法作用域, 也就是基于靜態(tài)的程序文本. 對一個塊中的某個名稱的任何賦值都會導(dǎo)致Python將對該名稱的全部引用當(dāng)做局部變量, 甚至是賦值前的處理. 如果碰到global聲明, 該名稱就會被視作全局變量.一個使用這個特性的例子:
def get_adder(summand1):
? ? """Returns a function that adds numbers to a given number."""
? ? def adder(summand2):
? ? ? ? return summand1 + summand2
? ? return adder
(譯者注: 這個例子有點(diǎn)詭異, 你應(yīng)該這樣使用這個函數(shù):sum?=?get_adder(summand1)(summand2))
優(yōu)點(diǎn):通??梢詭砀忧逦? 優(yōu)雅的代碼. 尤其會讓有經(jīng)驗(yàn)的Lisp和Scheme(還有Haskell, ML等)程序員感到欣慰.缺點(diǎn):可能導(dǎo)致讓人迷惑的bug. 例如下面這個依據(jù)PEP-0227的例子:
i = 4
def foo(x):
? ? def bar():
? ? ? ? print i,
? ? # ...
? ? # A bunch of code here
? ? # ...
? ? for i in x:? # Ah, i *is* local to Foo, so this is what Bar sees
? ? ? ? print i,
? ? bar()
因此foo([1,?2,?3])會打印1?2?3?3, 不是1?2?3?4.
(譯者注: x是一個列表, for循環(huán)其實(shí)是將x中的值依次賦給i.這樣對i的賦值就隱式的發(fā)生了, 整個foo函數(shù)體中的i都會被當(dāng)做局部變量, 包括bar()中的那個. 這一點(diǎn)與C++之類的靜態(tài)語言還是有很大差別的.)
結(jié)論:鼓勵使用.
函數(shù)與方法裝飾器
如果好處很顯然, 就明智而謹(jǐn)慎的使用裝飾器
定義:用于函數(shù)及方法的裝飾器?(也就是@標(biāo)記). 最常見的裝飾器是@classmethod 和@staticmethod, 用于將常規(guī)函數(shù)轉(zhuǎn)換成類方法或靜態(tài)方法. 不過, 裝飾器語法也允許用戶自定義裝飾器. 特別地, 對于某個函數(shù)my_decorator, 下面的兩段代碼是等效的:
class C(object):
? @my_decorator
? def method(self):
? ? ? # method body ...
class C(object):
? ? def method(self):
? ? ? ? # method body ...
? ? method = my_decorator(method)
優(yōu)點(diǎn):優(yōu)雅的在函數(shù)上指定一些轉(zhuǎn)換. 該轉(zhuǎn)換可能減少一些重復(fù)代碼, 保持已有函數(shù)不變(enforce invariants), 等.缺點(diǎn):裝飾器可以在函數(shù)的參數(shù)或返回值上執(zhí)行任何操作, 這可能導(dǎo)致讓人驚異的隱藏行為. 而且, 裝飾器在導(dǎo)入時執(zhí)行. 從裝飾器代碼的失敗中恢復(fù)更加不可能.結(jié)論:如果好處很顯然, 就明智而謹(jǐn)慎的使用裝飾器. 裝飾器應(yīng)該遵守和函數(shù)一樣的導(dǎo)入和命名規(guī)則. 裝飾器的python文檔應(yīng)該清晰的說明該函數(shù)是一個裝飾器. 請為裝飾器編寫單元測試.避免裝飾器自身對外界的依賴(即不要依賴于文件, socket, 數(shù)據(jù)庫連接等), 因?yàn)檠b飾器運(yùn)行時這些資源可能不可用(由pydoc或其它工具導(dǎo)入). 應(yīng)該保證一個用有效參數(shù)調(diào)用的裝飾器在所有情況下都是成功的.裝飾器是一種特殊形式的”頂級代碼”. 參考后面關(guān)于?Main?的話題.
線程
不要依賴內(nèi)建類型的原子性.
雖然Python的內(nèi)建類型例如字典看上去擁有原子操作, 但是在某些情形下它們?nèi)匀徊皇窃拥?即: 如果__hash__或__eq__被實(shí)現(xiàn)為Python方法)且它們的原子性是靠不住的. 你也不能指望原子變量賦值(因?yàn)檫@個反過來依賴字典).
優(yōu)先使用Queue模塊的Queue數(shù)據(jù)類型作為線程間的數(shù)據(jù)通信方式. 另外, 使用threading模塊及其鎖原語(locking primitives). 了解條件變量的合適使用方式, 這樣你就可以使用threading.Condition來取代低級別的鎖了.
威力過大的特性
避免使用這些特性
定義:Python是一種異常靈活的語言, 它為你提供了很多花哨的特性, 諸如元類(metaclasses), 字節(jié)碼訪問, 任意編譯(on-the-fly compilation), 動態(tài)繼承, 對象父類重定義(object reparenting), 導(dǎo)入黑客(import hacks), 反射, 系統(tǒng)內(nèi)修改(modification of system internals), 等等.優(yōu)點(diǎn):強(qiáng)大的語言特性, 能讓你的代碼更緊湊.缺點(diǎn):使用這些很”酷”的特性十分誘人, 但不是絕對必要. 使用奇技淫巧的代碼將更加難以閱讀和調(diào)試. 開始可能還好(對原作者而言), 但當(dāng)你回顧代碼, 它們可能會比那些稍長一點(diǎn)但是很直接的代碼更加難以理解.結(jié)論:在你的代碼中避免這些特性.
Python風(fēng)格規(guī)范
分號
不要在行尾加分號, 也不要用分號將兩條命令放在同一行.
行長度
每行不超過80個字符
例外:
長的導(dǎo)入模塊語句
注釋里的URL
不要使用反斜杠連接行.
Python會將?圓括號, 中括號和花括號中的行隱式的連接起來?, 你可以利用這個特點(diǎn). 如果需要, 你可以在表達(dá)式外圍增加一對額外的圓括號.
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'):
如果一個文本字符串在一行放不下, 可以使用圓括號來實(shí)現(xiàn)隱式行連接:
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
注意上面例子中的元素縮進(jìn); 你可以在本文的?縮進(jìn)?部分找到解釋.
括號
寧缺毋濫的使用括號
除非是用于實(shí)現(xiàn)行連接, 否則不要在返回語句或條件語句中使用括號. 不過在元組兩邊使用括號是可以的.
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)
縮進(jìn)
用4個空格來縮進(jìn)代碼
絕對不要用tab, 也不要tab和空格混用. 對于行連接的情況, 你應(yīng)該要么垂直對齊換行的元素(見?行長度?部分的示例), 或者使用4空格的懸掛式縮進(jìn)(這時第一行不應(yīng)該有參數(shù)):
Yes:? # Aligned with opening delimiter
? ? ? foo = long_function_name(var_one, var_two,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? var_three, var_four)
? ? ? # Aligned with opening delimiter in a dictionary
? ? ? foo = {
? ? ? ? ? long_dictionary_key: value1 +
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? value2,
? ? ? ? ? ...
? ? ? }
? ? ? # 4-space hanging indent; nothing on first line
? ? ? foo = long_function_name(
? ? ? ? ? var_one, var_two, var_three,
? ? ? ? ? var_four)
? ? ? # 4-space hanging indent in a dictionary
? ? ? foo = {
? ? ? ? ? long_dictionary_key:
? ? ? ? ? ? ? long_dictionary_value,
? ? ? ? ? ...
? ? ? }
No:? ? # Stuff on first line forbidden
? ? ? foo = long_function_name(var_one, var_two,
? ? ? ? ? var_three, var_four)
? ? ? # 2-space hanging indent forbidden
? ? ? foo = long_function_name(
? ? ? ? var_one, var_two, var_three,
? ? ? ? var_four)
? ? ? # No hanging indent in a dictionary
? ? ? foo = {
? ? ? ? ? long_dictionary_key:
? ? ? ? ? ? ? long_dictionary_value,
? ? ? ? ? ? ? ...
? ? ? }
空行
頂級定義之間空兩行, 方法定義之間空一行
頂級定義之間空兩行, 比如函數(shù)或者類定義. 方法定義, 類定義與第一個方法之間, 都應(yīng)該空一行. 函數(shù)或方法中, 某些地方要是你覺得合適, 就空一行.
空格
按照標(biāo)準(zhǔn)的排版規(guī)范來使用標(biāo)點(diǎn)兩邊的空格
括號內(nèi)不要有空格.
Yes: spam(ham[1], {eggs: 2}, [])
No:? spam( ham[ 1 ], { eggs: 2 }, [ ] )
不要在逗號, 分號, 冒號前面加空格, 但應(yīng)該在它們后面加(除了在行尾).
Yes: if x == 4:
? ? ? ? print x, y
? ? x, y = y, x
No:? if x == 4 :
? ? ? ? print x , y
? ? x , y = y , x
參數(shù)列表, 索引或切片的左括號前不應(yīng)加空格.
Yes: spam(1)
no: spam (1)
Yes: dict['key'] = list[index]
No:? dict ['key'] = list [index]
在二元操作符兩邊都加上一個空格, 比如賦值(=), 比較(==, <, >, !=, <>, <=, >=, in, not in, is, is not), 布爾(and, or, not). 至于算術(shù)操作符兩邊的空格該如何使用, 需要你自己好好判斷. 不過兩側(cè)務(wù)必要保持一致.
Yes: x == 1
No:? x<1
當(dāng)’=’用于指示關(guān)鍵字參數(shù)或默認(rèn)參數(shù)值時, 不要在其兩側(cè)使用空格.
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)
不要用空格來垂直對齊多行間的標(biāo)記, 因?yàn)檫@會成為維護(hù)的負(fù)擔(dān)(適用于:, #, =等):
Yes:
? ? foo = 1000? # comment
? ? long_name = 2? # comment that should not be aligned
? ? dictionary = {
? ? ? ? "foo": 1,
? ? ? ? "long_name": 2,
? ? ? ? }
No:
? ? foo? ? ? = 1000? # comment
? ? long_name = 2? ? # comment that should not be aligned
? ? dictionary = {
? ? ? ? "foo"? ? ? : 1,
? ? ? ? "long_name": 2,
? ? ? ? }
Shebang
大部分.py文件不必以#!作為文件的開始. 根據(jù)?PEP-394?, 程序的main文件應(yīng)該以 #!/usr/bin/python2或者 #!/usr/bin/python3開始.
(譯者注: 在計(jì)算機(jī)科學(xué)中,?Shebang?(也稱為Hashbang)是一個由井號和嘆號構(gòu)成的字符串行(#!), 其出現(xiàn)在文本文件的第一行的前兩個字符. 在文件中存在Shebang的情況下, 類Unix操作系統(tǒng)的程序載入器會分析Shebang后的內(nèi)容, 將這些內(nèi)容作為解釋器指令, 并調(diào)用該指令, 并將載有Shebang的文件路徑作為該解釋器的參數(shù). 例如, 以指令#!/bin/sh開頭的文件在執(zhí)行時會實(shí)際調(diào)用/bin/sh程序.)
#!先用于幫助內(nèi)核找到Python解釋器, 但是在導(dǎo)入模塊時, 將會被忽略. 因此只有被直接執(zhí)行的文件中才有必要加入#!.
注釋
確保對模塊, 函數(shù), 方法和行內(nèi)注釋使用正確的風(fēng)格
文檔字符串
Python有一種獨(dú)一無二的的注釋方式: 使用文檔字符串. 文檔字符串是包, 模塊, 類或函數(shù)里的第一個語句. 這些字符串可以通過對象的__doc__成員被自動提取, 并且被pydoc所用. (你可以在你的模塊上運(yùn)行pydoc試一把, 看看它長什么樣). 我們對文檔字符串的慣例是使用三重雙引號”“”(?PEP-257?). 一個文檔字符串應(yīng)該這樣組織: 首先是一行以句號, 問號或驚嘆號結(jié)尾的概述(或者該文檔字符串單純只有一行). 接著是一個空行. 接著是文檔字符串剩下的部分, 它應(yīng)該與文檔字符串的第一行的第一個引號對齊. 下面有更多文檔字符串的格式化規(guī)范.
模塊
每個文件應(yīng)該包含一個許可樣板. 根據(jù)項(xiàng)目使用的許可(例如, Apache 2.0, BSD, LGPL, GPL), 選擇合適的樣板.
函數(shù)和方法
下文所指的函數(shù),包括函數(shù), 方法, 以及生成器.
一個函數(shù)必須要有文檔字符串, 除非它滿足以下條件:
外部不可見
非常短小
簡單明了
文檔字符串應(yīng)該包含函數(shù)做什么, 以及輸入和輸出的詳細(xì)描述. 通常, 不應(yīng)該描述”怎么做”, 除非是一些復(fù)雜的算法. 文檔字符串應(yīng)該提供足夠的信息, 當(dāng)別人編寫代碼調(diào)用該函數(shù)時, 他不需要看一行代碼, 只要看文檔字符串就可以了. 對于復(fù)雜的代碼, 在代碼旁邊加注釋會比使用文檔字符串更有意義.
關(guān)于函數(shù)的幾個方面應(yīng)該在特定的小節(jié)中進(jìn)行描述記錄, 這幾個方面如下文所述. 每節(jié)應(yīng)該以一個標(biāo)題行開始. 標(biāo)題行以冒號結(jié)尾. 除標(biāo)題行外, 節(jié)的其他內(nèi)容應(yīng)被縮進(jìn)2個空格.
Args:列出每個參數(shù)的名字, 并在名字后使用一個冒號和一個空格, 分隔對該參數(shù)的描述.如果描述太長超過了單行80字符,使用2或者4個空格的懸掛縮進(jìn)(與文件其他部分保持一致). 描述應(yīng)該包括所需的類型和含義. 如果一個函數(shù)接受*foo(可變長度參數(shù)列表)或者**bar (任意關(guān)鍵字參數(shù)), 應(yīng)該詳細(xì)列出*foo和**bar.Returns: (或者 Yields: 用于生成器)描述返回值的類型和語義. 如果函數(shù)返回None, 這一部分可以省略.Raises:列出與接口有關(guān)的所有異常.
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
類
類應(yīng)該在其定義下有一個用于描述該類的文檔字符串. 如果你的類有公共屬性(Attributes), 那么文檔中應(yīng)該有一個屬性(Attributes)段. 并且應(yīng)該遵守和函數(shù)參數(shù)相同的格式.
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."""
塊注釋和行注釋
最需要寫注釋的是代碼中那些技巧性的部分. 如果你在下次?代碼審查?的時候必須解釋一下, 那么你應(yīng)該現(xiàn)在就給它寫注釋. 對于復(fù)雜的操作, 應(yīng)該在其操作開始前寫上若干行注釋. 對于不是一目了然的代碼, 應(yīng)在其行尾添加注釋.
# 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 iff i is a power of 2
為了提高可讀性, 注釋應(yīng)該至少離開代碼2個空格.
另一方面, 絕不要描述代碼. 假設(shè)閱讀代碼的人比你更懂Python, 他只是不知道你的代碼要做什么.
# BAD COMMENT: Now go through the b array and make sure whenever i occurs
# the next element is i+1
類
如果一個類不繼承自其它類, 就顯式的從object繼承. 嵌套類也一樣.
Yes: class SampleClass(object):
? ? ? ? pass
? ? class OuterClass(object):
? ? ? ? class InnerClass(object):
? ? ? ? ? ? pass
? ? class ChildClass(ParentClass):
? ? ? ? """Explicitly inherits from another class already."""
No: class SampleClass:
? ? ? ? pass
? ? class OuterClass:
? ? ? ? class InnerClass:
? ? ? ? ? ? pass
繼承自object是為了使屬性(properties)正常工作, 并且這樣可以保護(hù)你的代碼, 使其不受?PEP-3000?的一個特殊的潛在不兼容性影響. 這樣做也定義了一些特殊的方法, 這些方法實(shí)現(xiàn)了對象的默認(rèn)語義, 包括__new__,?__init__,?__delattr__,?__getattribute__,?__setattr__,?__hash__,?__repr__,?and?__str__.
字符串
即使參數(shù)都是字符串, 使用%操作符或者格式化方法格式化字符串. 不過也不能一概而論, 你需要在+和%之間好好判定.
Yes: x = a + b
? ? x = '%s, %s!' % (imperative, expletive)
? ? x = '{}, {}!'.format(imperative, expletive)
? ? x = 'name: %s; score: %d' % (name, n)
? ? x = 'name: {}; score: {}'.format(name, n)
No: x = '%s%s' % (a, b)? # use + in this case
? ? x = '{}{}'.format(a, b)? # use + in this case
? ? x = imperative + ', ' + expletive + '!'
? ? x = 'name: ' + name + '; score: ' + str(n)
避免在循環(huán)中用+和+=操作符來累加字符串. 由于字符串是不可變的, 這樣做會創(chuàng)建不必要的臨時對象, 并且導(dǎo)致二次方而不是線性的運(yùn)行時間. 作為替代方案, 你可以將每個子串加入列表, 然后在循環(huán)結(jié)束后用.join連接列表. (也可以將每個子串寫入一個cStringIO.StringIO緩存中.)
Yes: items = ['<table>']
? ? for last_name, first_name in employee_list:
? ? ? ? items.append('<tr><td>%s, %s</td></tr>' % (last_name, first_name))
? ? items.append('</table>')
? ? employee_table = ''.join(items)
No: employee_table = '<table>'
? ? for last_name, first_name in employee_list:
? ? ? ? employee_table += '<tr><td>%s, %s</td></tr>' % (last_name, first_name)
? ? employee_table += '</table>'
在同一個文件中, 保持使用字符串引號的一致性. 使用單引號’或者雙引號”之一用以引用字符串, 并在同一文件中沿用. 在字符串內(nèi)可以使用另外一種引號, 以避免在字符串中使用. GPyLint已經(jīng)加入了這一檢查.
(譯者注:GPyLint疑為筆誤, 應(yīng)為PyLint.)
Yes:
? ? Python('Why are you hiding your eyes?')
? ? Gollum("I'm scared of lint errors.")
? ? Narrator('"Good!" thought a happy Python reviewer.')
No:
? ? Python("Why are you hiding your eyes?")
? ? Gollum('The lint. It burns. It burns us.')
? ? Gollum("Always the great lint. Watching. Watching.")
為多行字符串使用三重雙引號”“”而非三重單引號’‘’. 當(dāng)且僅當(dāng)項(xiàng)目中使用單引號’來引用字符串時, 才可能會使用三重’‘’為非文檔字符串的多行字符串來標(biāo)識引用. 文檔字符串必須使用三重雙引號”“”. 不過要注意, 通常用隱式行連接更清晰, 因?yàn)槎嘈凶址c程序其他部分的縮進(jìn)方式不一致.
Yes:
? ? print ("This is much nicer.\n"
? ? ? ? ? "Do it this way.\n")
No:
? ? ? print """This is pretty ugly.
? Don't do this.
? """
文件和sockets
在文件和sockets結(jié)束時, 顯式的關(guān)閉它.
除文件外, sockets或其他類似文件的對象在沒有必要的情況下打開, 會有許多副作用, 例如:
它們可能會消耗有限的系統(tǒng)資源, 如文件描述符. 如果這些資源在使用后沒有及時歸還系統(tǒng), 那么用于處理這些對象的代碼會將資源消耗殆盡.
持有文件將會阻止對于文件的其他諸如移動、刪除之類的操作.
僅僅是從邏輯上關(guān)閉文件和sockets, 那么它們?nèi)匀豢赡軙黄涔蚕淼某绦蛟跓o意中進(jìn)行讀或者寫操作. 只有當(dāng)它們真正被關(guān)閉后, 對于它們嘗試進(jìn)行讀或者寫操作將會跑出異常, 并使得問題快速顯現(xiàn)出來.
而且, 幻想當(dāng)文件對象析構(gòu)時, 文件和sockets會自動關(guān)閉, 試圖將文件對象的生命周期和文件的狀態(tài)綁定在一起的想法, 都是不現(xiàn)實(shí)的. 因?yàn)橛腥缦略?
沒有任何方法可以確保運(yùn)行環(huán)境會真正的執(zhí)行文件的析構(gòu). 不同的Python實(shí)現(xiàn)采用不同的內(nèi)存管理技術(shù), 比如延時垃圾處理機(jī)制. 延時垃圾處理機(jī)制可能會導(dǎo)致對象生命周期被任意無限制的延長.
對于文件意外的引用,會導(dǎo)致對于文件的持有時間超出預(yù)期(比如對于異常的跟蹤, 包含有全局變量等).
推薦使用?“with”語句?以管理文件:
with open("hello.txt") as hello_file:
? ? for line in hello_file:
? ? ? ? print line
對于不支持使用”with”語句的類似文件的對象,使用 contextlib.closing():
import contextlib
with contextlib.closing(urllib.urlopen("http://www.python.org/")) as front_page:
? ? for line in front_page:
? ? ? ? print line
Legacy AppEngine 中Python 2.5的代碼如使用”with”語句, 需要添加 “from __future__ import with_statement”.
TODO注釋
為臨時代碼使用TODO注釋, 它是一種短期解決方案. 不算完美, 但夠好了.
TODO注釋應(yīng)該在所有開頭處包含”TODO”字符串, 緊跟著是用括號括起來的你的名字, email地址或其它標(biāo)識符. 然后是一個可選的冒號. 接著必須有一行注釋, 解釋要做什么. 主要目的是為了有一個統(tǒng)一的TODO格式, 這樣添加注釋的人就可以搜索到(并可以按需提供更多細(xì)節(jié)). 寫了TODO注釋并不保證寫的人會親自解決問題. 當(dāng)你寫了一個TODO, 請注上你的名字.
# TODO(kl@gmail.com): Use a "*" here for string repetition.
# TODO(Zeke) Change this to use relations.
如果你的TODO是”將來做某事”的形式, 那么請確保你包含了一個指定的日期(“2009年11月解決”)或者一個特定的事件(“等到所有的客戶都可以處理XML請求就移除這些代碼”).
導(dǎo)入格式
每個導(dǎo)入應(yīng)該獨(dú)占一行
Yes: import os
? ? import sys
No:? import os, sys
導(dǎo)入總應(yīng)該放在文件頂部, 位于模塊注釋和文檔字符串之后, 模塊全局變量和常量之前. 導(dǎo)入應(yīng)該按照從最通用到最不通用的順序分組:
標(biāo)準(zhǔn)庫導(dǎo)入
第三方庫導(dǎo)入
應(yīng)用程序指定導(dǎo)入
每種分組中, 應(yīng)該根據(jù)每個模塊的完整包路徑按字典序排序, 忽略大小寫.
import foo
from foo import bar
from foo.bar import baz
from foo.bar import Quux
from Foob import ar
語句
通常每個語句應(yīng)該獨(dú)占一行
不過, 如果測試結(jié)果與測試語句在一行放得下, 你也可以將它們放在同一行. 如果是if語句, 只有在沒有else時才能這樣做. 特別地, 絕不要對try/except這樣做, 因?yàn)閠ry和except不能放在同一行.
Yes:
? if foo: bar(foo)
No:
? if foo: bar(foo)
? else:? baz(foo)
? try:? ? ? ? ? ? ? bar(foo)
? except ValueError: baz(foo)
? try:
? ? ? bar(foo)
? except ValueError: baz(foo)
訪問控制
在Python中, 對于瑣碎又不太重要的訪問函數(shù), 你應(yīng)該直接使用公有變量來取代它們, 這樣可以避免額外的函數(shù)調(diào)用開銷. 當(dāng)添加更多功能時, 你可以用屬性(property)來保持語法的一致性.
(譯者注: 重視封裝的面向?qū)ο蟪绦騿T看到這個可能會很反感, 因?yàn)樗麄円恢北唤逃? 所有成員變量都必須是私有的! 其實(shí), 那真的是有點(diǎn)麻煩啊. 試著去接受Pythonic哲學(xué)吧)
另一方面, 如果訪問更復(fù)雜, 或者變量的訪問開銷很顯著, 那么你應(yīng)該使用像get_foo()和set_foo()這樣的函數(shù)調(diào)用. 如果之前的代碼行為允許通過屬性(property)訪問 , 那么就不要將新的訪問函數(shù)與屬性綁定. 這樣, 任何試圖通過老方法訪問變量的代碼就沒法運(yùn)行, 使用者也就會意識到復(fù)雜性發(fā)生了變化.
命名
module_name, package_name, ClassName, method_name, ExceptionName, function_name, GLOBAL_VAR_NAME, instance_var_name, function_parameter_name, local_var_name.
應(yīng)該避免的名稱
單字符名稱, 除了計(jì)數(shù)器和迭代器.
包/模塊名中的連字符(-)
雙下劃線開頭并結(jié)尾的名稱(Python保留, 例如__init__)
命名約定
所謂”內(nèi)部(Internal)”表示僅模塊內(nèi)可用, 或者, 在類內(nèi)是保護(hù)或私有的.
用單下劃線(_)開頭表示模塊變量或函數(shù)是protected的(使用import * from時不會包含).
用雙下劃線(__)開頭的實(shí)例變量或方法表示類內(nèi)私有.
將相關(guān)的類和頂級函數(shù)放在同一個模塊里. 不像Java, 沒必要限制一個類一個模塊.
對類名使用大寫字母開頭的單詞(如CapWords, 即Pascal風(fēng)格), 但是模塊名應(yīng)該用小寫加下劃線的方式(如lower_with_under.py). 盡管已經(jīng)有很多現(xiàn)存的模塊使用類似于CapWords.py這樣的命名, 但現(xiàn)在已經(jīng)不鼓勵這樣做, 因?yàn)槿绻K名碰巧和類名一致, 這會讓人困擾.
Python之父Guido推薦的規(guī)范
TypePublicInternal
Moduleslower_with_under_lower_with_under
Packageslower_with_under
ClassesCapWords_CapWords
ExceptionsCapWords
Functionslower_with_under()_lower_with_under()
Global/Class ConstantsCAPS_WITH_UNDER_CAPS_WITH_UNDER
Global/Class Variableslower_with_under_lower_with_under
Instance Variableslower_with_under_lower_with_under (protected) or __lower_with_under (private)
Method Nameslower_with_under()_lower_with_under() (protected) or __lower_with_under() (private)
Function/Method Parameterslower_with_under
Local Variableslower_with_under
Main
即使是一個打算被用作腳本的文件, 也應(yīng)該是可導(dǎo)入的. 并且簡單的導(dǎo)入不應(yīng)該導(dǎo)致這個腳本的主功能(main functionality)被執(zhí)行, 這是一種副作用. 主功能應(yīng)該放在一個main()函數(shù)中.
在Python中, pydoc以及單元測試要求模塊必須是可導(dǎo)入的. 你的代碼應(yīng)該在執(zhí)行主程序前總是檢查if?__name__?==?'__main__', 這樣當(dāng)模塊被導(dǎo)入時主程序就不會被執(zhí)行.
def main():
? ? ? ...
if __name__ == '__main__':
? ? main()
所有的頂級代碼在模塊導(dǎo)入時都會被執(zhí)行. 要小心不要去調(diào)用函數(shù), 創(chuàng)建對象, 或者執(zhí)行那些不應(yīng)該在使用pydoc時執(zhí)行的操作.