本系列來自《編寫高質量代碼 改善python程式的91個建議》的讀書筆記整理。python編程規範系列--建議08~18 ...
本系列來自《編寫高質量代碼 改善python程式的91個建議》的讀書筆記整理。
本章主要內容
建議8:利用assert語句來發現問題 建議9:數據交換值時不推薦使用中間交換變數 建議10:充分利用Lazy evaluation的特性 建議11:理解枚舉替代實現的缺陷 建議12:不推薦使用type來進行類型檢查 建議13:儘量轉換為浮點類型再做除法 建議14:警惕eval()的安全漏洞 建議15:使用enumerate()獲取序列迭代的索引和值 建議16:分清==與is的適用場景 建議17:考慮相容性,儘可能使用Unicod 建議18:構建合理的包層次來管理module
建議8:利用assert語句來發現問題
1)__debug__的值預設為True,且只讀,無法修改(py2.7)。 2)斷言是有代價的,對性能產生一定影響。禁用斷言的方法是在運行腳本的時候加上-O標記(不優化位元組碼,而是忽略與斷言相關的語句)。使用斷言註意點: 1)不要濫用,這是使用斷言的最基本的原則; 2)如果Python本身的異常能夠處理就不要再使用斷言; 3)不要使用斷言來檢查用戶的輸入; 4)在函數調用後,當需要確認返回值是否合理時可以使用斷言; 5)當條件時業務邏輯繼續下去的先決條件時,可以使用斷言。
建議9:數據交換值時不推薦使用中間交換變數
1 >>> from timeit import Timer 2 >>> Timer('temp=x;x=y;y=temp','x=2;y=3').timeit() 3 0.03472399711608887 4 >>> Timer('x,y=y,x','x=2;y=3').timeit() 5 0.031581878662109375
建議10:充分利用Lazy evaluation的特性
Lazy evaluation常被譯作“延時計算”或“惰性計算”,指的是僅僅在真正需要執行計算的時候才計算表達式的值。典型例子:生成器表達式。 1)避免不必要的計算,帶來性能上的提升; 2)節省空間,使用無限迴圈的數據結構成為可能。建議11:理解枚舉替代實現的缺陷
1)替代方法:使用類屬性;藉助函數;使用collections.namedtuple.1 >>> from collections import namedtuple 2 >>> Seasons=namedtuple('Seasons','Spring Summer Autumn Winter')._make(xrange(4)) 3 >>> print Seasons 4 Seasons(Spring=0, Summer=1, Autumn=2, Winter=3) 5 >>> print Seasons.Autumn 6 22)替代缺陷:允許枚舉值重覆;支持無意義的操作.
1 >>> Seasons._replace(Spring=2) # 不合理 2 Seasons(Spring=2, Summer=1, Autumn=2, Winter=3) 3 >>> Seasons.Summer+Seasons.Autumn == Seasons.Winter # 無意義 4 True3)py2.7的替代方案(py3.4後引入Enum類型):flufl.enum
1 from flufl.enum import Enum 2 3 4 class Seasons(Enum): 5 Spring = "Spring" 6 Summer = 2 7 Autumn = 3 8 Winter = 4 9 10 Seasons = Enum('Seasons', 'Spring Summer Autumn Winter') 11 print Seasons 12 print Seasons.Summer.value
建議12:不推薦使用type來進行類型檢查
1)基於內建類型擴展的用戶自定義類型,type函數並不能準確返回結果; 2)在舊式類中,所有類的實例的type值都相等。 3)可以用isinstance()函數檢查。建議13:儘量轉換為浮點類型再做除法
當涉及除法運算的時候儘量先將操作數轉換成浮點類型再做運算。 浮點數不精確性導致的無限迴圈:1 >>> i=1 2 >>> while i!=1.5: 3 ... i=i+0.1 4 ... print i
建議14:警惕eval()的安全漏洞
1 # -*-coding:UTF-8 -*- 2 3 import sys 4 from math import * 5 6 7 def ExpCalcBot(string): 8 try: 9 print 'Your answer is', eval(string) 10 except NameError: 11 print "The expression you enter is not valid." 12 13 14 while True: 15 print 'Please enter a number or operation. Enter e to complete. ' 16 17 inputStr = raw_input() 18 if inputStr == 'e': 19 sys.exit() 20 elif repr(inputStr) != ' ': 21 ExpCalcBot(inputStr)
輸入:__import__("os").system("dir") 顯示當前目錄下的所有文件.
__import__("os").system("del */Q") 刪除當前目錄下的所有文件.因此,在實際應用過程中國呢如果使用對象不是信任源,應該儘量避免使用eval,在需要使用eval的地方可以用安全性更好的ast.literal_eval替代。
建議15:使用enumerate()獲取序列迭代的索引和值
註意,在獲取迭代過程中字典的key和value,應該使用如下iteritems()方法。1 >>> person={'name': 'Josn', 'age': 19, 'hobby': 'football'} 2 >>> for k,v in person.iteritems(): 3 ... print k, ":", v
建議16:分清==與is的適用場景
1 >>> a="Hi" 2 >>> b="Hi" 3 >>> a is b 4 True 5 >>> a==b 6 True 7 >>> a1 ="I am using long string for testing" # 註意區分 8 >>> b1 ="I am using long string for testing" 9 >>> a1 is b1 10 False 11 >>> a1==b1 12 Trueis:表示的是對象標識符,檢查對象的標識符是否一致,也就是比較兩個對象在記憶體中是否擁有同一塊記憶體空間; ==:表示的是值相等,用來判斷兩個對象的值是否相等,可以被重載。 字元串駐留(string interning)機制:對於較小的字元串,為了提高系統性能會保留其值的一個副本,當創建新的字元串時直接指向該副本即可。
建議17:考慮相容性,儘可能使用Unicode
python內建的字元串有兩種類型:str和Unicode,共同祖先為basestring。 windows本地預設編碼是CP936。 解碼:str.decode([編碼參數[,錯誤處理]])編碼:str.encode([編碼參數[,錯誤處理]])
錯誤處理參數有3種方式:
(1)strict:預設值,拋出UnicodeError異常;
(2)ignore:忽略不可轉換的字元;
(3)replace:將不可轉換字元用?代替。
有些軟體在保存UTF-8編碼時,會在文件最開始地方插入不可見的BOM,可以利用codecs解決。
1 import codecs 2 3 4 content = open('manage.py', 'r').read() 5 6 if content[:3] == codecs.BOM_UTF8: 7 content = content[:3] 8 9 print content.decode("utf-8")編碼聲明的三種方式:
1 # coding=<encoding name> #方式一 2 #!/usr/bin/env python 3 4 # -*- coding:<encoding name> -*- #方式二 5 6 #!/usr/bin/env python 7 # vim:set fileencoding=<encoding name> #方式三
建議18:構建合理的包層次來管理module
包中__init__.py文件的作用:1)使包和普通目錄區分; 2)在該文件中聲明模塊級別的import語句從而使其變成包級別可見; 3)通過該文件中定義__all__變數,控制需要導入的子包或者模塊。使用包的好處: 1)合理組織代碼,便於維護和使用; 2)能夠有效地避免名稱空間衝突。