建議 8:利用assert語句來發(fā)現(xiàn)問題
# 斷言用法
assert expression
# 等價于以下語句
if not expression:
raise AssertionError
使用斷言注意以下幾點
1 不要濫用,這是基本原則。若由于斷言已發(fā)了異常,通常代表程序中存在bug。因此斷言應(yīng)該使用在正常邏輯不可能到達的地方活正常情況下總是為真的場合。
2 如果Python本身的異常能夠處理就不要再使用斷言。如對于類似數(shù)組越界、類型匹配、除數(shù)為0之類的錯誤,不建議使用斷言來處理。下面的例子中使用斷言就顯得多余,因為如果傳入的參數(shù)一個為串串,另一個為數(shù)字或者列表,本身就會拋出TypeError。
def stradd(x, y):
assert isinstance(x, basestring)
assert isinstance(y, basestring)
return x+y
3 不要使用斷言來檢查用戶的輸入。如對于一個數(shù)字類型,如果根據(jù)用戶的設(shè)計,該值的范圍是2~10,較好的做法是使用條件判斷,并在不符合條件的時候輸出錯誤提示信息。
4 在函數(shù)調(diào)用后,當需要確認返回值是否合理時可以使用斷言
5 當條件是業(yè)務(wù)邏輯進行下去的先決條件時,可以使用斷言。如list1和其副本list2,業(yè)務(wù)繼續(xù)下去的條件是這兩個list必須是一樣的,但由于某些不可控因素,如使用了淺拷貝而且list1中含有尅安對象,就可以使用斷言來判斷這兩者的關(guān)系,如果相等,則繼續(xù)運行后面的程序意義不大。
建議9:數(shù)據(jù)交換不推薦使用中間變量
# Python推薦使用, 性能更好
x ,y = y, x
# 大家熟悉方式
temp = x
x = y
y = temp
建議10:充分利用Lazy evaluation的特性
1 避免不必要的計算,以便帶來性能上的提升。比如以下示例:
# 當x為False的情況,y將不再計算
if x and y:
pass
2 節(jié)省空間,使得無限循環(huán)的數(shù)據(jù)結(jié)構(gòu)成為可能。如斐波那契數(shù)列的實現(xiàn)
def fib():
a, b = 0, 1
while True:
yield a
a, b = b, a+b
from itertools import islice
print(list(islice(fib(), 5)))
建議11:理解枚舉替代實現(xiàn)的缺陷
在Python3.4以前并沒提供枚舉類型,于是人們充分利用Python的動態(tài)性這個特征,想出來枚舉的各種替代實現(xiàn)方式。
1 使用類屬性
class Seasons:
Spring = 0
Summer = 1
Autumn = 2
Winter = 3
2 借助函數(shù)
# 定義
def enum(*posarg, **keysarg):
return type("Enum", (object,), dict(zip(posag, xrange(len(posarg))), **keysarg))
# 使用
Seasons = enum("Spring", "Summer", "Autumn", Winter=1)
print Seasons.Spring # 0
3 使用collections.namedtuple
Seasons = namedtuple("Seasons", "Spring Summer Autumn Winter")._make(range(4))
Seasons.Spring # 0
枚舉代替實現(xiàn)方式還有很多,這些方案也有其不合理的地方。
1 枚舉值不唯一
2 帶來一些無意義的操作,如 Seasons.Summer + Seasons.Autumn == Seasons.Winter
還有一種替代方案,第三方模塊 flufl.enum,
請參考: http://Pythonhosted.org/flufl.enum/docs/using.html
建議12: 不推薦使用type來進行類型檢查
不刻意進行類型檢查,而是在出錯的情況下通過拋出異常來進行處理,是較為常見的方式。但實際應(yīng)用中為了提高程序的健壯性,仍然會面臨需要類型檢查的情況。那么用什么方法呢。
1
type(), 所有基本類型對應(yīng)的名稱都可以在types模塊中找到,如types.BooleanType, types.IntType, types.StringType等。為什么不推薦使用呢,因為基于內(nèi)建類型擴展的用戶自定義類型,type函數(shù)并不能準確返回結(jié)果。
if type(a) is types.ListType:
pass
2 isinstance
isinstance(2, float) # False
isinstance("a", (str, unicode)) # True
isinstance((2, 3), (str, list, tuple)) # True
建議13:盡量轉(zhuǎn)換為浮點類型后再做除法
在Python2中,借鑒了C預(yù)研的一些規(guī)則,兩個整數(shù)相除,返回值也是整數(shù),運算結(jié)果將直接截斷。Python3中做了一定的修正,已經(jīng)不存在這個問題了。
建議14: 警惕eval()的安全漏洞
如果使用對象不是信任源,應(yīng)該盡量避免使用eval,在使用eval的地方可用安全性更好的ast.literal_evalt替代??梢詤⒖嘉臋n:http://docs.python.org/2/library/ast.html#ast.literal_eval
建議15:使用enumerate()獲取序列迭代的索引和值
使用enumerate(),代碼清晰簡潔,可讀性最好。它具有一定的惰性,每次盡在需要的時候財貨產(chǎn)生一個(index, item)對。
li = ["a", "b", "c", "d", "e"]
for i, e in enumerate(li):
print "index: ", i, "element: ", e
enumerate()函數(shù)的內(nèi)如實現(xiàn)非常簡單,enumerate(sequence, start=0)實際相當于如下代碼
def enumerate(sequence, start=0):
n = start
for elem in sequence:
yield n, elem
n += 1
對于字典的迭代循環(huán),enumerate()并不適合,輸出結(jié)果與期望大相徑庭
person_info = {"name": "Jon", "age": "20"}
for k, v in enumerate(person_info):
print k, v
# 輸出為(Mac, Python2.7.15)
0 name
1 age
要獲取字典迭代過程中的key、value,應(yīng)該使用如下items()方法:
for k, v in person_info.items():
print k, v
建議16:分清 == 與 is 的適用場景
1 is 表示對象標識符
2 == 表示值是否相等
# 當a、b的值同為短字符串時,a is b為True,python的字符串主流機制,他們指向同一個對象
a = "I am using long string for testing"
b = "I am using long string for testing"
a is b # False
a == b # True
建議17:考慮兼容性,盡可能使用Unicode
對于A、B兩種編碼系統(tǒng),相互轉(zhuǎn)換示意圖如下

建議18:構(gòu)建合理的包層次來管理module
本質(zhì)上每一個Python文件都是一個模塊,使用模塊可以增強代碼的可維護性和可重用性。
什么是包呢?簡單的說包即是目錄,它除了包含常規(guī)的Python文件以外,還包含一個__init__.py
1 合理組織代碼,便于維護和使用
2 能夠有效地避免名稱空間沖突
3 以下一個可供參考的Python項目結(jié)構(gòu)
ProjectName/
| --- README
| --- LICENSE
| --- setup.py
| --- sample/
| | --- __init__.py
| | --- core.py
| | --- helpers.py
| --- docs/
| | --- config.py
| | --- index.rst
| --- bin/
| --- package/
| | --- __init__.py
| | --- subpackage/
| | --- ......
| --- tests/
| | --- test_basic.py
| | --- test_advanced.py