巧用python上下文管理器

前言

首先问下帅气的读者们,当你们需要访问一个文件时候,你们是用什么方式访问的,是open吗?如果是,那么你们有没有觉得每次都需要 ‘手动’ 关闭很麻烦或则忘记关闭很麻烦。今天我们带大家来认识python一个新的魔法---上文管理器。如果你知道那么不要急着走开,自己心里默默的问自己是不是可以回答以下几个问题:

  1. 上下文管理器是什么?
  2. 上下文管理器怎么实现,你是否有多种方式来实现呢?
  3. 为什么要用上下文管理器?

上下文管理器是什么

概念

上下文管理器:Python2.5开始支持的一种语法,用于规定某个对象的使用范围。一旦进入或者离开该使用范围,会有特殊操作被调用 (比如为对象分配或者释放内存)。它的语法形式是with...as..

语法

首先我们先简单的看一段使用上下文管理器的代码:

with open('帅气的读者名单.txt', 'w') as f:
    f.write('读者名字')

从上面可以看出语法结构是:

with expre as var:
    context

上下文管理器怎么实现

类实现了__enter____exit__的方法

这里我们就以实现处理文件为例子吧

class OperateFile(object):
    def __init__(self, file_name, operate_type):
        print('初始化')
        self.file_name = file_name
        self.operate_type = operate_type
        self.fp = None

    def __enter__(self):
        print('调用 __enter__ ')
        self.fp = open(self.file_name, self.operate_type)
        return self.fp

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('调用 __exit__ ')
        if self.fp:
            self.fp.close()


if __name__ == "__main__":
    with OperateFile(path, "w+") as f:
        f.write("帅气的读者")

执行结果

初始化
调用 __enter__ 
调用 __exit__ 

从上面可以看出,在编写代码时,__enter__返回需要管理的资源,__exit__ 通常做释放资源的操作。

可以简单来说:

__enter__: 进入与此对象相关的上下文。如果存在该方法,with语法会把该方法的返回值作为绑定到as子句中指定的变量上

__exit__: 退出对象对应的上下文

另外注意的是: __exit__()中有四个参数,当程序块中出现异常(exception),__exit__()的参数中exc_type(异常类型), exc_value(异常值), traceback(异常的错误栈信息)用于描述异常。

基于生成器实现上下文管理器 

在python中除了通过类实现__enter____exit__的方法来实现上下文管理器外,我们也可以使用contextlib.contextmanager装饰器来实现基于生成器的上下文管理器,接下来我们就简单看看吧。

这里我们就以实现链接数据库为例子吧

from contextlib import contextmanager
import pymysql


@contextmanager
def db_manager(host, port, user, password, database):
    try:
        db_mysql = pymysql.connect(host=host, user=user, password=password, database=database, port=port)
        yield db_mysql
    finally:
        db_mysql.close()

这段代码中,函数 db_manager() 是一个生成器,当我们执行 with 语句时,便会打开文件,并返回文件对象 db_mysql;当 with 语句执行完后,finally block 中的关闭文件操作便会执行。

注意:基于生成器定义的上下文管理需要使用装饰器 @contextmanager,不再使用生成器协议方法。

两者区别

使用类来实现相较于生成器实现更加灵活,在复杂度更高更大型的项目可以使用,反之可以使用生成器来实现

为什么要用上下文管理器

第一,我们可以更加优雅的操作(创建/获取/释放)资源,如文件操作、数据库连接等等;

第二,可以避免内存泄露,首先我们看下面的这个例子

for x in range(10000000):
    f = open('./帅气的读者名单.txt', 'w')
    f.write('帅气的读者名字')

因为程序中同时打开了太多的文件,占据了太多的资源,造成系统崩溃。所以报错

OSError: [Errno 23] Too many open files in system: '帅气的读者名单.txt'

ps:在这里我简单的说句,你别说怎么可能会出现这样的错误,我个人的角度,人是一个及其不可控的因素,很多bug都是不小心引起的

第三,可以更加优雅的处理异常

class ErrorCapture():
    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print("exc_type", exc_type)
        print("exc_val", exc_val)
        print("exc_tb", exc_tb)
        return True

    def operate(self):
        1 / 0


with ErrorCapture() as res:
    res.operate()

在这个例子里,我们可以选择展示告警(也可以根据情况设定不同返回信息),也可以选择不展示直接写入日志等等的处理方式

总的来说使用上下文管理器可以:

  1. 提高代码复用率;
  2. 提高代码可读性;
  3. 避免一些人为因素引起的问题

学习链接

深入理解 Python 中的上下文管理器

Python深入02 上下文管理器

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木子林_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值