背景:常用的with语句允许开发者创建上下文管理器。什么是上下文管理器?上下文管理器就是允许你可以自动地开始和结束一些事情。例如,你可能想要打开一个文件,然后写入一些内容,最后再关闭文件。这或许就是上下文管理器中一个最经典的示例。事实上,当你利用with语句打开一个文件时,Python替你自动创建了一个上下文管理器。
1、with语句
最常用的方式 打开一个文件,with语句在你写入文件后替你关闭了文件。
不使用with语句
f = open("1.txt","w")
f.write("hello world")
f.close()
使用with语句
with open("1.txt","w") as f:
f.write("hello world")
# 其实上面的代码等价于 try--finally语句
try:
f = open('1.txt', 'w')
f.write('hello world')
finally:
if f:
f.close()
上下文管理器背后工作的机制是使用Python的方法:enter__和__exit。
2、使用__enter__()和__exit__()实现自定义类实现open方法
上例中open()方法是Python自带的,那我们怎么定义自己的类型来使用with语句呢。其实只要你的类定义了__enter__()和__exit__()方法,就可以使用Python的上下文管理器了。enter()方法会在with语句进入时被调用,其返回值会赋给as关键字后的变量;而__exit__()方法会在with语句块退出后自动被调用。
# 上面的with语句 等价于下面的代码
class OpenFile(object):
def __init__(self, filename):
self.file_name = filename
def __enter__(self):
self.f = open(self.file_name, 'a+')
return self.f
def __exit__(self, exc_type, exc_val, exc_tb):
self.f.close()
with OpenFile('1.txt') as f:
f.write('hello world')
3、使用contextlib.ContextManager 替代__enter__()和__exit__()实现open方法
这个装饰器常与yield一起用
@contextlib.contextmanager是一个装饰器,由它修饰的方法会有两部分构成,中间由yield关键字分开。由此方法创建的上下文管理器,在代码块执行前会先执行yield上面的语句;在代码块执行后会再执行yield下面的语句。
import contextlib
@contextlib.contextmanager
def file_open(path):
try:
f = open(path,"w")
yield f
except Exception as e:
print(e.message)
finally:
print("Closing file")
if f:
f.close()
if __name__ == "__main__":
with file_open("1.txt") as f:
f.write("hello world")
4、耗时统计demo
import contextlib
import time
@contextlib.contextmanager
def cost_time(title):
print('cost time start...')
start = time.time()
yield
print('cost time end...')
end = time.time()
usedTime = (end - start) * 1000
print('cost time %d ms' % usedTime)
print('--'*20)
with cost_time(1):
print('3...')
time.sleep(1)
with cost_time(2):
print('4...')
time.sleep(2)
输出结果如下:
cost time start...
3...
cost time end...
cost time 1000 ms
----------------------------------------
cost time start...
4...
cost time end...
cost time 2000 ms
----------------------------------------
5、实际项目案例 创建数据库链接
import contextlib
import MySQLdb
@contextlib.contextmanager
def connect_db(dict_cursor=True):
conn, cursor = None, None
try:
conf = {
"db_host": "xxx",
"db_port": "xxx",
"db_user": "xxx",
"db_pswd": "xxx",
"db_name": "xxx",
}
conn = MySQLdb.connect(
host=conf["db_host"], port=int(conf["db_port"]), user=conf["db_user"],
passwd=conf["db_pswd"], db=conf["db_name"], charset="utf8", autocommit=False)
cursor = conn.cursor(MySQLdb.cursors.DictCursor) if dict_cursor else conn.cursor()
yield cursor
conn.commit()
except Exception as e:
if cursor:
cursor.close()
raise e
finally:
if cursor:
cursor.close()
if conn:
conn.close()
参考链接:
https://www.cnblogs.com/zknublx/p/11726572.html