深入理解python上下文管理器及对contextlib的运用

什么是上下文管理器

    要弄清楚什么是上下文管理器,我们要先深入理解什么是上下文。

    于是鲁肃引从者径投南郡来,到城下叫门。赵云出问,肃曰:“我要见刘玄德有话
说。”
    请分析鲁肃此时对刘备情感的微妙变化?

    我分析你个**b的,光这一句话我怎么知道他的情感变化。

    一篇文章,给你摘录一段,没前没后,你读不明白。

为啥呢,因为你不知道发生了啥事。一段话说了什么,要通过上下文(文章的上下文)来推断。

    程序的上下文也差不多,大多函数都有很多外部变量,并不是程序本身传过来的。一旦你的一段程序有了外部变量,这段程序就不完整,不能独立运行。你为了使他们运行,就要给所有的外部变量一个一个写一些值进去。这些值的集合就叫上下文。

    比如我现在需要对一些外部数据集进行改动,但是数据集是外部的。也许是别人提供的,也许是爬取的。我并不能保证数据集的格式和结构是我所预期的,程序只会按着预定的流程去处理这些数据集,这样往往会存在不可预期的错误。

    再比如我们对远程的数据进行操作过程中我也并不能保证通信是否正常,我们并不知道什么时候程序会出错,什么时候会通讯故障。

    所以我们会在程序的内部写一些try-except以求能提高程序的容错性,数据库则提供了事务来保证数据的安全。但这时我们的程序又会变得十分的臃肿。

    如果这个时候有一个统一的特殊程序来管理某一段程序或者某一批类似的程序。这样我程序就不需要考虑这些问题,我只需要把我内部的业务实现清楚而不需要考虑外部的逻辑,这一段特殊的程序就是我们所说的上下文管理器。

python中经典的上下文管理器

文档流的运用

    这个函数大家肯定不会陌生,其经典的语法是

with open(path,'wb') as f:
    text = f.read()

    这个是经典的文件io流处理了,在对文件进行操作完成后,会自动为我们关闭文档对象释放资源。

path = "2.txt"
with open(path,'r') as f:
    text = f.read()
    print("with内的f我是类",f)
    print('with内的text我是文本',text)

print("with外的f我是类",f)
print("with的text我是文本",text)

try:
    f.read()
except Exception as e:
    print("位置语句外尝试操作f出现异常: ",e)

在这里插入图片描述
    那么这个f是什么呢,是上下文管理器吗?其实f是一个资源操作的类,我们能把文本的信息读出来在with语句后面的代码调用,并且f这个类依然也存在。但是在with语句之后,我们无法再通过f对资源进行操作,因为资源已经关闭了。

flask的运用
@app.route('/')
def index():
    user_agent = request.headers.get('User-Agent')
    return '<p>Your browser is %s</p>' % user_agent

    这里的request其实就是一个上下文的全局变量,index函数调用了request这个资源,但是这个资源既不是传进来的,更不是引入的数据类型(字典,数据,json)等,而这个资源也是必须当index函数被推送之后,请求上下文才生成的。关于具体的实现,这篇文章不做深入探索,如果感兴趣的话可以看我的另一篇flask文章。

构造上下文管理器

class Resource():

    def __enter__(self):
        print('文件打开')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('文件关闭')
        print("exc_type:{},exc_val:{},exc_tb:{}".format(exc_type,exc_val,exc_tb))

    def read(self):
        print('文件读取')

if __name__ == "__main__":
    with Resource() as res:
        res.read()
    print("程序结束")

在这里插入图片描述
    构造上下文管理器也十分简单,我们只需要在类中定义__enter__、__exit__函数即可。
那如果当我们读取失败时会如何处理呢。

with Resource() as res:
    1/0
    res.operate()
print("程序结束")

在这里插入图片描述
    我们可以看到文章正常的关闭,并打印出了异常的信息,但是后面的程序却不再执行了,如果想让后面的程序继续执行该怎么办呢,其实也很简单,我们只需要在__exit__函数中返回一个True就可以了

  def __exit__(self, exc_type, exc_val, exc_tb):

        print("exc_type:{},exc_val:{},exc_tb:{}".format(exc_type,exc_val,exc_tb))
        print('文件关闭')
        return True

在这里插入图片描述
    这时候后续的函数就正常的执行了,有人说上下文管理器其实就是对python 的 try except finally的优雅写法,我理解这种说法,但不认同。事实上上下文管理器要做的事很复杂,上述的例子并不能完整的阐述其原理。在其他语言中或者系统中,均有上下文的运用。
    另外再提一句,上述的res是什么?是上下文管理器吗?不是哈,他只是资源,这点一定要理解清楚。

更加优雅的上下文管理器的写法

    python的哲学就是优雅精巧,如果每次构造上下文管理器都要去写类函数很显然不够优雅,所以python提供了一种更加简便的书写方式。

from contextlib import contextmanager
class Resource():

    def operate(self):
        print('文件读取')

@contextmanager
def resource():
    print('文件打开')
    try:
        yield Resource()
    except Exception as e:
        print(e)
    finally:
        print('文件关闭')


if __name__ == "__main__":
    with resource() as res:
        1/0
        res.operate()

    print("程序结束")

真正使用

    纸上学来终觉浅,才知进步靠实战,学习了一个新知识尤其是你认可的新知识还是要把他应用起来,下面是我自己封装的一个数据库io的上下文管理器,写的可能不好,希望能帮助到你。

@contextmanager
def deal_with(sql):
    connect = pymysql.connect()
    cursor = connect.cursor()
    try:
        cursor.execute(sql)
        result = cursor.fetchall()
        connect.commit()
        yield result
    except Exception as e:
        connect.rollback() 
        print('事务处理失败', e)
    finally:
        print('关闭连接') 
        cursor.close()
        connect.close()

    如果感觉文章对你有所帮助,还请不吝赐赞。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值