python | 改善Python程序建議- Part2

這個系列主要是總結一些《改善python程序的91個建議》的學習筆記,但願能夠對本身和讀者有所幫助。本文是該系列第二部分,第一部分請見 Part1html

21 多使用else 讓程序變的更加pythonic
eg: try-except-else-finally
 
22 異常處理注意點
· 注意異常粒度,不推薦在try中放入過多的代碼 最好保持異常粒度的一致性和合理性
· 異常捕獲儘可能具體指定,少用Except 使用單獨的except 最好可以使用raise將異常拋出
· 注意異常捕獲的順序 (…)
· 使用更爲友好的異常信息,遵照異常參數的規範

23 避免finally中可能發生的陷阱python

finally中產生新的異常或者執行了return or break 那麼臨時報錯的異常就會丟失,致使異常屏蔽

24 深刻理解None,正確判斷對象是否爲空函數

python 如下數據會當作空來處理:
· 常量None 常量False
· 任何形式的數值類型0 eg:0,0L ,0.0,0j
· 空的序列字典,如」(),[],{}
· 當用戶定義的類中定義了nonzero()和len()方法,而且該方法返回bool值false或者整數0
   常量None的特殊性體如今它 既不是0orFalse,它就是一個空值對象,數據類型爲NoneType,遵循單例模式
   None與任何其餘非None的對象比較結果爲False None !={}
   通常判斷空列表 直接 if list or if len(list)

25 鏈接字符串應優先使用join而不是+ —-> 字符串規模比較大的時候性能

26 python 格式化字符串儘可能使用 str.format 而不是%
eg: 
‘xxxx{0}xxxx{1}’.format(n1,n2)
最直接的理由:% 最終會被.format 方式代替
 
27 區別對待可變對象和不可變對象
· 不可變對象: 數字,字符串,元組
· 可變對象 : 字典,列表,字節數
· 注意:列表的切片操做至關於淺拷貝 —>list1=[1,2,3] list2=list1[:] id(list1)!=id(list2)
 
28 使用列表解析 like [x for x in range(10)]
· 代碼更簡潔
· 效率更高 可是大數據不是最佳的選擇,過多的內存消耗可能會致使memoryError
 
29 警戒默認參數潛在的問題
def xxx(xx,a=[]) 不要使用列表等可變類型 def實際是可執行語句 默認參數也會被計算
===>應修改成 def xxx(xx,a=None)
 
30 慎用變長參數
· 使用過於靈活,可能破壞程序的健壯性
· 若是一個函數的參數列表很長,雖然能夠經過使用*args 和 **kwargs 來簡化函數的定義
   但一般這意味着這個函數能夠有更好的實現方式,應該被重構,用一個序列來保存須要傳遞的參數
適合場景是: 裝飾器 讀取配置文件
 
31 深刻理解str()和repr()的區別
· str()主要面向用戶,目的是可讀性,返回可讀性強的字符串類型
   repr()面向的是Python解釋器or 開發人員,返回值表示python解釋器內部的含義
· 在解釋器中直接輸入a時默認調用repr()函數,而print a 則 調用的是str()
· 通常來講類中毒定義了__repr__()方法,而__str__()則爲可選,沒有__str__(),則默認會使用__repr__()
 
32 分清staticmethod和classmethod的適用場景
· 靜態方法:實現功能不和實例相關也不和類相關的獨立方法,如一些字符驗證等,定義在類中能有效將代碼組織起來,從而使相關代碼的 垂直距離更近,提升代碼的 可維護性;固然,若是有一組獨立的方法,將其定義在一個模塊中,經過模塊來訪問這些方法也是不錯的選擇
· 類方法: 減小子類的代碼重寫, 下降代碼的冗餘
 
33 字符串的基本用法
· 多行字符串使用下面的方式
s = (
    'aaaaa'
    'bbbb'
    'ccccc'
)

· 3對雙引號會把換行符和前導空格當作字符串的一部分學習

· str.startswith和endswith中匹配參數可使用元組,有一個匹配上就返回T大數據

· 判斷字符串包含子串的判斷推薦使用 in 和 not in
·  ''.split() ==> [] ''.split(' ') ==> ['']
 
34 按需選擇sort()或者sorted()
· sorted()會保留原有列表生成新的列表
· sorted功能很是強大,而sort只能排通常的列表
 
35 使用Counter 進行計數統計

from collections import Counter
some_data = ['a','2',2,4,5,'2','b',4,7,'a',5,'d','a','z']
print Counter(some_data)
>>>Counter({'a': 3, '2': 2, 4: 2, 5: 2, 2: 1, 'b': 1, 7: 1, 'd': 1, 'z': 1})

· Counter類屬於字典類的子類,是一個容器對象,主要用來統計散列對象。優化

# 取值
xx = Counter(some_data)
print xx.['a']

36 單例模式ui

保證系統中一個類只有一個實例並且該實例易於被外界訪問,從而方便對實例個數的控制並節約系統資源
class Test(object):
    is_instance = None
    is_first = True

    def __new__(cls, *args, **kwargs):
        if cls.is_instance == None:
            cls.is_instance = object.__new__(cls)

        return cls.is_instance

    def __init__(self, name):
        if Test.is_first:
            self.name = name
            Test.is_first = False


test1 = Test('lf1')

print(id(test1))       # 2136967041656
print(test1.name)      # lf1

test2 = Test('lf2')     

print(id(test2))        # 2136967041656
print(test2.name)       # lf1

37 利用cProfile 定位性能瓶頸spa

程序運行慢的緣由有不少,但真正的緣由每每是一兩段設計並不那麼良好的不起眼的程序。程序性能影響每每符合8/2法則,即20%的代碼運行時間佔用了80%的總運行時間(實際上,比例要誇張得多,一般是幾十行代碼佔用了95%以上的運行時間)。
profile是python的標準庫,能夠統計程序裏每一函數的運行時間,並提供了多樣的報表,cprofile則是它的C實現版本,剖析過程自己須要消耗的資源更少。
## test_cprofile.py
import time
def foo():
    sum = 0
    for i in range(100):
        sum += i
        time.sleep(.5)
    return sum
if __name__ == "__main__":
    foo()
如今使用profile分析這個程序
if __name__ =="__main__":
    import cProfile
    cProfile.run("foo()")

輸出以下:.net

第二種方式:python -m cProfile test_cprofile.py

· cProfile 的統計結果以及各項意義

統計項 意義
ncalls 函數的調用次數
tottime 函數總計運行時間,不含調用的函數運行時間
percall 函數運行一次的平均時間,等於tottime/ncalls
cumtime 函數總計運行時間,含調用的函數運行時間
percall 函數運行一次的平均時間,等於cumtime/ncalls
filename:lineno(function) 函數所在的文件名,函數的行號,函數名
  將cProfile的輸出保存到文件,並以各類形式來查看結果.
  1)使用cProfile.run()函數再提供一個實參,就是保存輸出的文件名;一樣,在命令行參數裏,多一個參數,用來保存cProfile的輸出
  2)經過pstats模塊的另外一個類Stats來解決.
# ....略
if __name__ == "__main__":
    import cProfile
    cProfile.run("foo()", "prof.txt")
    import pstats
    p = pstats.Stats("prof.txt")
    # sort_stats(key,[...]) 以key排序  print_stats()輸出結果
    p.sort_stats("time").print_stats()
  sort_stats的key

參數 參數的意義
ncalls 被調用次數
cumulative 函數運行的總時間
file 文件名
module 模塊名
pcalls 簡單調用統計(兼容舊版,未統計遞歸調用)
line 行號
name 函數名
nfl Name,file,line
stdname 標準函數名
time 函數內部運行時間(不計調用子函數的時間)

38 掌握循環優化的基本技巧

· 減小循環內部的計算

# 1
for i in range(iter):
    d = math.sqrt(y)
    j += i*d

# 2   比第一種快 40%~60%
d = math.sqrt(y)
for i in range(iter):
    j += i*d

· 將顯式循環改成隱式循環

"""求等差數列1,2,3,...n的和"""
# 1 
sum = 0
n = 10
for i in range(n+1):
    sum = sum + i

print(sum)

# 2   直接用數學知識 n*(n+1)/2  負面影響就是犧牲了代碼的可讀性 需添加清晰和恰當的註釋是很是必要的
n = 10 
print(n*(n+1)/2)

· 在循環中儘可能引用局部變量.在python命名空間中局部變量優先搜索,所以局部變量的查詢會比全局變量要快

# 1
x = [10,34,56,78]
def f(x):
    for i in range(len(x)):
        x[i] = math.sin(x[i])
    return x

# 2  性能提升10%~15%
def g(x):
    loc_sin = math.sin
    for i in range(len(x)):
        x[i] = loc_sin(x[i])
    return x

· 關注內層嵌套循環,儘可能將內層循環的計算往上層移,減小內層的計算

# 1
for i in range(len(v1)):
    for j in range(len(v2)):
        x = v1p[i]+ v2[j]

# 2
for i in range(len(v1)):
    vli = v1[i]
    for j in range(len(v2)):
        x = vli + v2[j]

 

參考:

《改善python程序的91個建議》

https://blog.csdn.net/qq_31603575/article/details/80177153

https://www.cnblogs.com/cotyb/p/5452602.html

https://www.cnblogs.com/cotyb/tag/pythonic/

https://medium.freecodecamp.org/an-a-z-of-useful-python-tricks-b467524ee747

相關文章
相關標籤/搜索