1. 程式人生 > >Effective python(五):內建模組

Effective python(五):內建模組

#### 1,考慮使用contextlib和with語句改寫可複用的try/finally程式碼 >1. `with lock:print('lock is held')`相當於`try:print('lock is held')`,`finally:lock.release`,使用with可以避免繁瑣的語句 >2. 開發者可以使用內建的contextlib模組的contextmanager修飾器來處理自己編寫的物件和函式以支援with語句,這樣做比標準寫法更便捷,如果使用標準方式寫,需要定義新類並提供__enter__和__exit__方法 > ```python > from contextlib import contextmanager > > #上下文管理器 > @contextmanager > def test(): > print("初始化") > #可以在這裡開啟資源,如log等級上升 > try: > print("with開始") > #如果有錯誤會通過yield彈出 > yield > finally: > print("離開時釋放資源") > > with test(): > print("go") > ``` >3. 可以在yield處彈出一個物件,通過as指定為一個區域性變數,在with內可以對其進行互動 #### 2,使用copyreg實現可靠的pickle操作 >1. python內建的pickle模組能將python物件轉化為位元組流,也能把位元組反序列化為python物件,但pickle處理後的資料實際上就是一個程式,可能會混入惡意資訊,對程式造成損壞,json產生的資料是一種安全的資訊,只描述物件如何構成,不會造成額外風險,所以pickle處理的位元組流不應該在未受信任的程式之間傳播 >2. 例如定義一個`GameState`類,例項化為物件`state=GameState()`包含玩家的當前遊戲狀態,生命,金幣等等,玩家退出遊戲的時候,將state直接寫到一份檔案裡,遊戲載入時讀取 > ```python > with open(path,'wb') as f: > pickle.dump(state,f) > > with open(path,'rb') as f: > state_after=pickle.load(f) > print(state_after.__dict__) > ``` >3. 如果類新增加了一些屬性,但儲存的物件仍然是舊的,那就需要使用copyreg了,先將類新增一個`__init__`構造器,通過傳參的方式初始化屬性,然後使用如下程式碼註冊函式,序列化和反序列化仍然按照原來的方式使用即可 > ```python > def unpickle_game_state(kwargs): > return GameState(**kwargs)#返回State例項化 > > def pickle_game_state(game_state):#引數為State物件 > kwargs=game_state.__dict__#獲取其屬性 > > #序列化封裝需要返回反序列化的函式和引數 > return unpickle_game_state,(kwargs,) > > copyreg.pickle(GameState,pickle_game_state)#註冊pickle函式 > ``` >4. 使用版本號管理類,修改copyreg註冊的pickle函式,在裡面新增一個版本號的引數`kwargs['version]=2`,然後在反序列化函式中根據版本號對其進行操作,即可相容版本 >5. 固定引入路徑,若重構時,將類名修改或刪除,那麼反序列化的時候會出錯,使用copyreg.pickle註冊後,會自動指向unpickle函式,所以不用擔心修改類名的問題,但如果沒有使用copyreg註冊,那麼修改類名後反序列化就會報錯 #### 4,使用datetime模組處理本地時間而不是time模組 >1. time模組,內建的time模組中有一個名叫localtime的函式,可以把UNIX時間戳(timestamp即UTC時刻距離UNIX計時原點的秒數)轉換為宿主計算機時區的當地時間,這個模組不夠穩定,只能轉換主機時區的時間,其它地區會出錯,應該儘量不用,而是使用datetime模組 > ```python > from time import localtime,strftime,strptime,mktime > time_format='%Y-%m-%d %H:%M:%S' > time_str=strftime(time_format,localtime(1407694710)) > #將時間戳轉換為當地時間 > print(time_str) > > #將本地時間轉化為UTC時間 > #strptime解析時間字串,mktime將本地時間轉換為UNIX時間戳 > print(mktime(strptime(strptime(time_str,time_format)))) > ``` >2. datetime模組 > ```python > from datetime import datetime,timezone > from time import mktime > > #UTC時間轉本地時間 > now=datetime(2014,8,10,18,18,30) > now_utc=now.replace(tzinfo=timezone.utc) > #注意此處若想可靠的轉換時區,還需要搭配pytz模組 > now_local=now_utc.astimezone()#此處只包含UTC時區 > print(now_local) > > #本地時間轉UTC格式時間戳 > time_str='2014-08-10 11:18:30' > time_format='%Y-%m-%d %H:%M:%S' > now=datetime.strptime(time_str,time_format) > time_tuple=now.timetuple() > utc_now=mktime(time_tuple) > print(time_tuple) > ``` >3. 若要在不同時區之間執行可靠的轉換操作,還需要搭配pytz模組使用 #### 5,內建的資料結構與演算法 (開發者不應該自己去重新實現,因為很難把他們寫好) >1. 雙向佇列,collections模組中的deque類,從該佇列頭部與尾部插入或移除一個元素,只有O(1)的時間複雜度 > ```python > d=deque() > d.append(1) > x=d.popleft() > ``` >2. 有序字典,collections模組中的OrderedDict類,它能夠按照鍵的插入順序,保留鍵值對在字典中的次序。標準字典是無序的,也就是說,在相同鍵值對的兩個字典上迭代可能出現不同的迭代順序 > ```python > a=OrderedDict() > a['x']=1 > ``` >3. 預設值字典,collections模組中的defaultdict類,本例中用int函式建立字典,預設值為0 > ```python > stats=defaultdict() > stats['my_counter']+=1 > ``` >4. 堆佇列(優先佇列),heapq模組中的heappush、heappop和nsmallest等函式,能夠在標準的list中建立堆結構,時間複雜度O(logn),普通列表O(n) > ```python > a=[] > heappush(a,5) > heappush(a,3) > heappush(a,7) > heappush(a,4) > #總是能彈出優先順序較高的元素, > #預設是越小元素優先順序越高 > print(heappop(a)) > #即使呼叫sort後依然能保持堆結構 > a.sort() > ``` >5. 二分查詢,bisect模組中的bisect_left函式,使用Index查詢複雜度為O(n),二分為O(logn),注意:使用前列表應排好序,bisect搜尋一百萬個元素的列表,與index搜尋包含14個元素的列表,所耗時間差不多 > ```python > i_index = bisect_left(alist,number) > ``` >6. 與迭代器有關的工具,itertools模組,可分為三大類 >>♦ 能夠把迭代器連線起來的函式: >>- chain:將多個迭代器按順序連成一個迭代器 >>- cycle:無限重複某個迭代器中的各個元素 >>- tee:把一個迭代器拆分成多個平行的迭代器 >>- zip_longest:與內建的zip函式相似,可以應對不同長度的迭代器 >>♦ 能夠從迭代器中過濾元素的函式: >>- islice:在不復制的前提下,根據索引值來切割迭代器獲取迭代器的一部分 >>- takewhile:在判定函式為True的時候,從迭代器逐個返回元素 >>- dropwhile:從判定函式初次為False的地方開始,逐個返回迭代器中的元素 >>- filterfalse:從迭代器中逐個返回能令判定函式為False的所有元素,效果與filter函式相反 >>♦ 能夠把迭代器中的元素組合起來的函式: >>- product:根據迭代器中的元素計算笛卡爾積(就是x*y),並返回。可以使用product改寫深度巢狀的列表推導操作 >>- permutations:用迭代器中的元素構建長度為N的有序排列,例:`permutations('ABCD',2) # AB AC AD BA BC BD CA CB CD DA DB DC` >>- combination:用迭代器中的元素構建長度為N的無序組合,例:`combinations('ABCD', 2) # AB AC AD BC BD CD` >備註:如果發現自己需要編寫一段非常麻煩的迭代程式,應該花些時間看看itertools的文件看看有沒有現成的工具可以用 #### 6,在重視精度的場合,使用decimal模組中的Decimal類,該類預設提供28個小數位,以進行定點數學運算,有需要可以把精度提的更高 >1. `dec=Decimal('1.45')`在decimal之間進行計算會得到精確的結果,計算後的型別仍然是Decimal型別,`print(dec)` >2. Decimal類提供quantize內建函式,它可以按照精度和舍入方式精確的調整數值`result=dec.quantize(Decimal('0.01'),rounding=ROUND_UP)`,`print(result)` >3. 若要使用精度不受限的方式表達有理數,那麼可以考慮使用Fraction類,包含在內建的fractions模組裡 #### 7,學會使用pypi,Python中央倉庫:https://pypi.python.org >1. 如果碰到不熟悉的程式設計難題,應該去PyPI裡看看別人的程式碼 >2. pip3安裝python3版本的軟體包,pip安裝python2版本的