了解with语句
在python中经常会看到with
语句,最常见的with
语句应该是with open(...)
,用过这个语句的人应该都知道这个语句意味着什么,with
语句开始之后首先会初始化一些对象,在退出with
语句之后会释放掉资源。
with
语句就是用来管理资源的,开始with
语句会创建资源,创建资源在__enter__
方法之中,with
语句结束之后会释放资源,释放资源在__exit__
方法之中。一般情况下,判断一个对象能不能使用with
语句就是判断该对象有没有实现这两个方法,比如定义一个类,该类实现了__enter__
和__exit__
方法:
class Manager:
def __enter__(self):
...
def __exit__(self, exc_type, exc_val, exc_tb):
...
现在Manager
的实例对象就可以使用with
语句来管理了,为了使用方便,在标准库contextlib
有很多简单易用的管理函数,介绍其中一个最常用的装饰函数contextmanager
,该装饰函数用来装饰一个生成器,在yield
语句之后返回资源。看一看示例:
""" with语句的本质
with exp1 [as exp2]:
do_something
类实现了__enter__方法和__exit__方法就是一个上下文管理器
在with exp1 [as exp2]语句中执行__enter__方法
在do_something之后执行__exit__方法
使用contextlib.contextmanager函数装饰器装饰一个生成器来创建上下文管理器
@contextlib.contextmanager
def generate(...):
<setup>
try:
yield <value>
finally:
<cleanup>
the statistics of this file:
lines(count) understand_level(h/m/l) classes(count) functions(count) fields(count)
000000000085 ----------------------m 00000000000001 0000000000000002 ~~~~~~~~~~~~~
"""
import time
import sqlite3
from contextlib import contextmanager
__author__ = '与C同行'
class ContextDb:
def __init__(self, db):
self.db = db
def __enter__(self):
self.cursor = self.db.cursor()
return self.cursor
def __exit__(self, exc_type, exc_val, exc_tb):
self.cursor.close()
self.db.close()
@contextmanager
def db_context_manager(db):
cursor = db.cursor()
try:
yield cursor
finally:
cursor.close()
db.close()
def print_pretty_outcome(cursor):
print('-' * 80)
all_outcome = cursor.fetchall()
for column_desc in cursor.description:
print(f'{column_desc[0]:<20}', end='')
print()
for item in all_outcome:
for cell in item:
try:
print(f'{cell:<20}', end='')
except TypeError:
cell = 'None'
print(f'{cell:<20}', end='')
print()
print('-' * 80)
print()
if __name__ == '__main__':
print(f'当前时间:{time.ctime()}')
print()
conn = sqlite3.connect('test.db')
print('使用类创建with上下文管理器')
with ContextDb(conn) as c:
c.execute('select 3*3')
print_pretty_outcome(c)
conn = sqlite3.connect('test.db')
print('使用contextmanager装饰器创建上下文管理器')
with db_context_manager(conn) as c:
c.execute('select 2+2')
print_pretty_outcome(c)
结果如下:
上面的示例创建了一个ContextDb
类,该类实现了__enter__
和__exit__
方法,在__enter__
方法之中返回游标,在__exit__
方法之中关闭游标和数据库。另外创建了一个db_context_manager
生成器,该生成器用contextmanager
装饰,在yield
语句之后返回游标,在finally
语句之后关闭游标和数据库。
喜欢python的朋友可以关注微信公众号“与C同行”: