with
Python 的 with
语句支持通过上下文管理器
所定义的运行时上下文这一概念。
此对象的实现使用了一对专门方法,允许用户自定义类来定义运行时上下文,在语句体被执行前进入该上下文,并在语句执行完毕时退出该上下文。
例如,文件对象的快速打开和关闭:
with open(file_path, 'r', encoding='utf8') as f:
for line in f:
....
上下文管理器
上下文管理器
是一个对象,它定义了在执行 with
语句时要建立的运行时上下文。
上下文管理器处理进入(enter
)和退出(exit
)所需运行时上下文以执行代码块。
__enter__
进入运行时
上下文并返回此对象或关联到该运行时上下文的其他对象。
此方法的返回值
会绑定到使用此上下文管理器的 with
语句的 as
子句中的标识符
__exit__
退出运行时
上下文并返回一个布尔值旗标来表明所发生的任何异常是否应当被屏蔽。
如果在执行 with
语句的语句体期间发生了异常,则参数会包含异常的类型、值以及回溯信息
一个简单的上下文管理器
import time
class RunTime:
def __init__(self, event):
self.event = event
def __enter__(self):
self.start = time.time()
def __exit__(self, exc_ty, exc_val, exc_tb):
self.end = time.time()
print('function:{},used time:{}s'.format(self.event, self.end - self.start))
可用于统计函数运行的时长
>>> with RunTime('test'):
... a = 0
... for _ in range(10000):
... a += 1
function:test,used time:0.0009884834289550781s
解释
编写上下文管理器的主要原理是你的代码会放到 with
语句块中执行。
当出现with
语句的时候,对象的 __enter__()
方法被触发, 它返回的值(如果有的话)会被赋值给 as
声明的变量。然后,with
语句块里面的代码开始执行。 最后,__exit__()
方法被触发进行清理工作。
不管 with
代码块中发生什么,上面的控制流都会执行完,就算代码块中发生了异常也是一样的。
事实上,__exit__()
方法的三个参数包含了异常类型、异常值和追溯信息(如果有的话)
。
__exit__()
方法能自己决定怎样利用这个异常信息,或者忽略它并返回一个None
值。
如果 __exit__()
返回 True
,那么异常会被清空,就好像什么都没发生一样, with
语句后面的程序继续在正常执行。
用途
在需要管理一些资源比如文件、网络连接和锁的编程环境中,使用上下文管理器是很普遍的。
这些资源的一个主要特征是它们必须被手动地关闭或释放来确保程序的正确运行。 例如,如果你请求了一个锁,那么你必须确保之后释放了它,否则就可能产生死锁。 通过实现 __enter__()
和 __exit__()
方法并使用 with
语句可以很容易的避免这些问题, 因为 __exit__()
方法可以让你无需担心这些了。
快速实现上下文管理器
实现一个新的上下文管理器的最简单的方法就是使用 contexlib
模块中的 @contextmanager
装饰器。
import time
from contextlib import contextmanager
@contextmanager
def run_time(event):
start = time.time()
try:
yield
finally:
end = time.time()
print('function:{},used time:{}s'.format(event, end - start))
在函数run_time()
中,yield
之前的代码会在上下文管理器中作为 __enter__() 方法执行, 所有在 yield
之后的代码会作为 __exit__() 方法执行。 如果出现了异常,异常会在yield
语句那里抛出。
下面是一个更加高级一点的上下文管理器
@contextmanager
def session_maker(_session=session):
try:
yield _session
_session.commit()
except Exception as e:
_session.rollback()
print('error', e)
finally:
_session.close()
每次调用,session自动commit和关闭,保证数据的安全和完整
from contextlib import contextmanager
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
DB_CONFIG= {
'username': 'root',
'password': 'root',
'ip': '127.0.0.1',
'port': 3306,
'db': 'data',
}
Base = declarative_base()
engine = create_engine(
'mysql+pymysql://{username}:{password}@{ip}:{port}/{db}'.format(**DB_CONFIG),
encoding='utf-8',
echo=False,
)
Session = sessionmaker(bind=engine)
session = Session()
@contextmanager
def session_maker(_session=session):
try:
yield _session
_session.commit()
except Exception as e:
_session.rollback()
print('error', e)
finally:
_session.close()
if __name__ == '__main__':
with session_maker() as db_session:
result = db_session.query(Model).all()
...