什么是上下文管理器?
代码的环境就是上下文,实现了上下文管理器协议的类产生的实例就是上下文管理器对象。
在类中声名enter和exit方法的就时实现了上下文管理器协议,也就是说,只要定义了这两个方法,这个类的实例就是上下文管理器对象。
在数据库中的应用
在不适用上下文管理器对象时,我们一般按照连接数据库,sql操作,读取数据,断开连接这样的顺序进行编码,可是连接数据库和断开连接这两部是所有操作都必须要做的事,所以当有大量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()
上面这段代码中,创建了一个数据库类Database,里面定义了一个connected属性,用来表示是否已经连接上数据库。
connect方法代表连接数据库操作。close方法代表关闭数据库连接。query方法代表查询操作。
这是最容易想到,也是最基础的操作逻辑。
生成器也能解决这个问题
生成器本质上是一个方法,就像在你所写的函数上套了一层接口。看一个简单示例
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可以更优雅的解决这个问题
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())
# 调用handle_query()
handle_query()
可以明显的看到,Database这个类中加入了enter和exit方法。
这样Database()就产生了一个实现了上下文管理器协议的对象,就可以使用with语句了。
with语句会自动处理连接和断开。
with语句怎么就自动处理了呢?
当然还是需要我们使用代码来控制的。首先with Database() as db: 这句代码中的db,就是enter方法的返回值。
而且执行这句话的时候代码会先执行enter方法,这时就做了连接数据库的操作。
然后才是执行了print("handle...", db.query())这句代码,表示处理查询sql。
当print语句(也就是查询语句)执行完了,会回调exit方法,在这里写上关闭数据库连接的代码。
这样我们就不用无限制的写连接和断开的代码了。完美解决。
这个exit方法很安全,因为当with语句中的函数体引发了异常时,也会调用eixt方法,这样就会断开数据库的连接了。
网友评论