什么是上下文管理器?
代碼的環(huán)境就是上下文,實現(xiàn)了上下文管理器協(xié)議的類產(chǎn)生的實例就是上下文管理器對象。
在類中聲名enter和exit方法的就時實現(xiàn)了上下文管理器協(xié)議,也就是說,只要定義了這兩個方法,這個類的實例就是上下文管理器對象。
在數(shù)據(jù)庫中的應(yīng)用
在不適用上下文管理器對象時,我們一般按照連接數(shù)據(jù)庫,sql操作,讀取數(shù)據(jù),斷開連接這樣的順序進行編碼,可是連接數(shù)據(jù)庫和斷開連接這兩部是所有操作都必須要做的事,所以當(dāng)有大量sql時,按照這樣的邏輯些代碼會造成大量的冗余代碼。
我們先來看一下最原始的寫法:
class Database():
def __init__(self):
self.connected = False
def connect(self):
self.connected = True
def close(self):
self.connected = False
def query(self):
if self.connected:
return "query data"
else:
raise ValueError("DB not connected.")
def handle_query():
db = Database()
db.connect()
print("handling......", db.query())
db.close()
上面這段代碼中,創(chuàng)建了一個數(shù)據(jù)庫類Database,里面定義了一個connected屬性,用來表示是否已經(jīng)連接上數(shù)據(jù)庫。
connect方法代表連接數(shù)據(jù)庫操作。close方法代表關(guān)閉數(shù)據(jù)庫連接。query方法代表查詢操作。
這是最容易想到,也是最基礎(chǔ)的操作邏輯。
生成器也能解決這個問題
生成器本質(zhì)上是一個方法,就像在你所寫的函數(shù)上套了一層接口。看一個簡單示例
def dbconn(fn):
def wrapper(*args, **kwargs):
db = Database()
db.connect()
ret = fn(db, *args, **kwargs)
db.close()
return ret
return wrapper
@dbconn
def handle_query(db=None):
print("handle---", db.query())
with可以更優(yōu)雅的解決這個問題
class Database():
def __init__(self):
self.connected = False
def connect(self):
self.connected = True
def close(self):
self.connected = False
def query(self):
if self.connected:
return "query data"
else:
raise ValueError("DB not connected.")
def __enter__(self):
self.connect()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.close()
# 使用with來解決
def handle_query():
with Database() as db:
print("handle...", db.query())
# 調(diào)用handle_query()
handle_query()
可以明顯的看到,Database這個類中加入了enter和exit方法。
這樣Database()就產(chǎn)生了一個實現(xiàn)了上下文管理器協(xié)議的對象,就可以使用with語句了。
with語句會自動處理連接和斷開。
with語句怎么就自動處理了呢?
當(dāng)然還是需要我們使用代碼來控制的。首先with Database() as db: 這句代碼中的db,就是enter方法的返回值。
而且執(zhí)行這句話的時候代碼會先執(zhí)行enter方法,這時就做了連接數(shù)據(jù)庫的操作。
然后才是執(zhí)行了print("handle...", db.query())這句代碼,表示處理查詢sql。
當(dāng)print語句(也就是查詢語句)執(zhí)行完了,會回調(diào)exit方法,在這里寫上關(guān)閉數(shù)據(jù)庫連接的代碼。
這樣我們就不用無限制的寫連接和斷開的代碼了。完美解決。
這個exit方法很安全,因為當(dāng)with語句中的函數(shù)體引發(fā)了異常時,也會調(diào)用eixt方法,這樣就會斷開數(shù)據(jù)庫的連接了。