python with上下文管理器

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()
        ...

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值