一、errno
在日常開發中經常需要捕獲各種異常,做特殊處理。舉個例子:
os.kill(12345,0)
輸出
Traceback (most recent call last):
File "/Users/zhangkun/Documents/GitHub/geektime/test.py", line 4, in <module>
os.kill(12345,0)
ProcessLookupError: [Errno 3] No such process
信號爲0,表示這只是檢查PID的有效性。根據提示,這是一個“Nosuchprocess”類型的錯誤,注意方括號中的“Errno3”,這其實是Python內置的錯誤系統提供的編號:
In:os.strerror(3)
Out:'Nosuchprocess'
還可以使用errno模塊找到對應的錯誤類型,更精準地做異常處理(errno_example.py):
import os
import errno
def listdir(dirname):
try:
os.listdir(dirname)
except OSError as e:
error = e.errno
if error == errno.ENOENT:
print('no such file or directory')
elif error == errno.EACCES:
print('Permission denied')
elif error == errno.ENOSPC:
print('No space left on device')
else:
print(e.strerror)
for filename in ['/no/such/dir', '/root', '/home/ubuntu']:
listdir(filename)
通過對比異常對象的errno屬性值就能知道異常類型。
二、subprocess
subprocess模塊用來取代如下模塊和函數:
os.system
os.spawn*
os.popen*
popen2.*
commands.*
1.call:執行系統命令,可以替代os.system。call只返回命令的返回值。
In:subprocess.call('ls-l/tmp/mongodb27017.sock',shell=True)
srwx------1 mongodbmongodb0May1012:24/tmp/mongodb27017.sock
Out:0
In:subprocess.call('exit1',shell=True)
Out:1
通常由於安全問題,不建議使用shell=True,可以把命令拆分成列表
In:subprocess.call(['ls','/tmp/mongodb27017.sock'],shell=False)
/tmp/mongodb27017.sock
Out:0
拆分命令最簡單的方法是使用shlex模塊:
In:shlex.split('ls/tmp/mongodb27017.sock')
Out:['ls','/tmp/mongodb27017.sock']
2.check_call:添加了錯誤處理的執行系統命令方法。當執行call方法的返回值不爲0,就會拋出CalledProcessError異常。
3.Popen:一個用來執行子進程的類,通過communicate方法獲得執行結果:
4. 4.check_output:在Python2.7和Python3中都可用,它比Popen更簡單地獲得輸出,但是需要執行的返回值爲0,否則仍然拋出CalledProcessError異常。
在出現錯誤的時候,可以額外地執行exit0,就能正常獲得輸出:
三、contextlib
寫Python代碼的時候經常將一系列操作放在一個語句塊中,Python2.5加入了with語法,實現上下文管理功能,這讓代碼的可讀性更強並且錯誤更少。最常見的例子就是open,如果不使用with,使用open時會是這樣
import threading
lock = threading.Lock()
lock.acquire()
my_list = []
item = 1
try:
my_list.append(item)
finally:
lock.release()
如果使用with就會非常簡單
with lock:
my_list.append(item)
創建上下文管理器實際就是創建一個類,添加__enter__和__exit__方法。看看threading.Lock的上下文管理功能是怎麼實現的:
import threading
class LockContext(object):
def __init__(self):
print('__init__')
self.lock=threading.Lock()
def __enter__(self):
print('__enter__')
self.lock.acquire()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('__exit__')
self.lock.release()
with LockContext():
print('in the context')
輸出:
__init__
__enter__
in the context
__exit__
上面的例子比較簡單,在執行self.__enter__的時候沒有傳遞參數。下面我們來實現open的上下文管理功能:
自定義上下文管理器確實很方便,但是Python標準庫還提供了更易用的上下文管理器工具模塊contextlib,它是通過生成器實現的,我們不必再創建類以及__enter__和__exit__這兩個特殊的方法:
from contextlib import contextmanager
@contextmanager
def make_open_context(filename,mode):
fp = open(filename,mode)
try:
yield fp
finally:
fp.close()
with make_open_context('/tmp/a','a') as f:
f.write('hello world')
yield關鍵詞把上下文分割成兩部分:yield之前就是__init__中的代碼塊;yield之後其實就是__exit__中的代碼塊;yield生成的值會綁定到with語句as子句中的變量(如果沒有生成,也就沒有as字句)。
四、glob
glob用來匹配UNIX風格的路徑名字的模塊,它支持“”、“?”、“[]”這三種通配符,“”代表0個或多個字符,“?”代表一個字符,“[]”匹配指定範圍內的字符,例如[09]匹配數字。我們先創建一個目錄,目錄中包含如下文件:
五、operator
operator是一個內建操作的函數式接口。舉個例子,如果想對列表的值求和,可以使用sum,但是如果要相乘呢?其實可以結合reduce和operator模塊來實現:
operator模塊還提供了非常有用的itemgetter、attrgetter和methodcaller方法。
- itemgetter。通過被求值對象的__getitem__方法獲得符合的條目:
另一個使用itemgetter的場景是排序:
2.attrgetter。attrgetter根據被求值對象的屬性獲得符合條件的結果:
再簡單一點:
reduce(getattr,'b,c,d'.split('.'),a)
而使用attrgetter就很簡潔:
operator.attrgetter('b.c.d')(a)
3.methodcaller。methodcaller將調用被求值對象的方法:
六、functools
functools模塊中包含了一系列操作其他函數的工具。
1.partial。partial可以重新定義函數簽名,也就是在執行函數之前把一些參數預先傳給函數,待執行時傳入的參數數量會減少。
2.wraps。把被封裝函數的__name__、module、__doc__和__dict__複製到封裝函數中,這樣在未來排錯或者函數自省的時候能夠獲得正確的源函數的對應屬性,所以使用wraps是一個好習慣。我們先看不使用wraps的例子:
3.total_ordering。對比自定義對象需要添加__lt__、le、gt、ge__和__eq__等方法,如果使用total_ordering,只需要定義__eq,以及定義__lt__、le、gt、__ge__四種方法之一就可以了:
4.cmp_to_key。Python2的sorted函數除了通過指定key參數的值作爲依據來排序,還支持cmp參數:
除了兼容Python3的sorted,還可以把比較函數轉換後用於min、max、heapq.nlargest、heapq.nsmallest、itertools.groupby等支持key參數的函數上。
七、collections
collections包含5個高性能的計算器工具
1. Counter 一個方便、快速計算的計時器工具
可以直接list()將Counter類型對象轉爲列表
2.deque:一個雙端隊列,能夠在隊列兩端添加或刪除隊列元素。它支持線程安全,能夠有效利用內存。無論從隊列的哪端入隊和出隊,性能都能夠接近於O(1)。
3.defaultdict。defaultdict簡化了處理不存在的鍵的場景。如果不使用defaultdict,對一個單詞的計數要這樣實現:
defaultdict參數就是值的類型,還可以使用自定義類型。下面演示一個插入後自動排序的自定義列表類型:
4.OrderedDict。Python的dict結構是無序的:
5.namedtuple。namedtuple能創建可以通過屬性訪問元素內容的擴展元組。使用namedtuple能創建更健壯、可讀性更好的代碼。
假設不使用namedtuple,查詢數據庫獲得如下一條記錄: