侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

Python中的上下文管理器相关知识详解

Python  /  管理员 发布于 7年前   282

前言

with 这个关键字,对于每一学习Python的人,都不会陌生。

操作文本对象的时候,几乎所有的人都会让我们要用 with open ,这就是一个上下文管理的例子。你一定已经相当熟悉了,我就不再废话了。

with open('test.txt') as f:  print f.readlines()

什么是上下文管理器?

基本语法

with EXPR as VAR:  BLOCK

先理清几个概念

1. 上下文表达式:with open('test.txt') as f:

2. 上下文管理器:open('test.txt')

3. f 不是上下文管理器,应该是资源对象。

如何写上下文管理器?

要自己实现这样一个上下文管理,要先知道上下文管理协议。

简单点说,就是在一个类里,实现了__enter__和__exit__的方法,这个类的实例就是一个上下文管理器。

例如这个示例:

class Resource():  def __enter__(self):    print('===connect to resource===')    return self  def __exit__(self, exc_type, exc_val, exc_tb):    print('===close resource connection===')       def operate(self):    print('===in operation===')     with Resource() as res:  res.operate()

我们执行一下,通过日志的打印顺序。可以知道其执行过程。
===connect to resource==
===in operation===
===close resource connection===

从这个示例可以很明显的看出,在编写代码时,可以将资源的连接或者获取放在__enter__中,而将资源的关闭写在__exit__ 中。

为什么要用上下文管理器?

学习时多问自己几个为什么,养成对一些细节的思考,有助于加深对知识点的理解。

为什么要使用上下文管理器?

在我看来,这和 Python 崇尚的优雅风格有关。

  • 可以以一种更加优雅的方式,操作(创建/获取/释放)资源,如文件操作、数据库连接;
  • 可以以一种更加优雅的方式,处理异常;

第一种,我们上面已经以资源的连接为例讲过了。

而第二种,会被大多数人所忽略。这里会重点讲一下。

大家都知道,处理异常,通常都是使用 try...execept.. 来捕获处理的。这样做一个不好的地方是,在代码的主逻辑里,会有大量的异常处理代理,这会很大的影响我们的可读性。

好一点的做法呢,可以使用 with 将异常的处理隐藏起来。

仍然是以上面的代码为例,我们将1/0 这个一定会抛出异常的代码写在 operate 里

class Resource():  def __enter__(self):    print('===connect to resource===')    return self   def __exit__(self, exc_type, exc_val, exc_tb):    print('===close resource connection===')    return True   def operate(self):    1/0 with Resource() as res:  res.operate()

运行一下,惊奇地发现,居然不会报错。

这就是上下文管理协议的一个强大之处,异常可以在__exit__ 进行捕获并由你自己决定如何处理,是抛出呢还是在这里就解决了。在__exit__ 里返回 True(没有return 就默认为 return False),就相当于告诉 Python解释器,这个异常我们已经捕获了,不需要再往外抛了。

在 写__exit__ 函数时,需要注意的事,它必须要有这三个参数:

  • exc_type:异常类型
  • exc_val:异常值
  • exc_tb:异常的错误栈信息

当主逻辑代码没有报异常时,这三个参数将都为None。

理解并使用 contextlib

在上面的例子中,我们只是为了构建一个上下文管理器,却写了一个类。如果只是要实现一个简单的功能,写一个类未免有点过于繁杂。这时候,我们就想,如果只写一个函数就可以实现上下文管理器就好了。

这个点Python早就想到了。它给我们提供了一个装饰器,你只要按照它的代码协议来实现函数内容,就可以将这个函数对象变成一个上下文管理器。

我们按照 contextlib 的协议来自己实现一个打开文件(with open)的上下文管理器。

import contextlib @contextlib.contextmanagerdef open_func(file_name):  # __enter__方法  print('open file:', file_name, 'in __enter__')  file_handler = open(file_name, 'r')     # 【重点】:yield  yield file_handler   # __exit__方法  print('close file:', file_name, 'in __exit__')  file_handler.close()  return with open_func('/Users/MING/mytest.txt') as file_in:  for line in file_in:    print(line)

在被装饰函数里,必须是一个生成器(带有yield),而yield之前的代码,就相当于__enter__里的内容。yield 之后的代码,就相当于__exit__ 里的内容。

上面这段代码只能实现上下文管理器的第一个目的(管理资源),并不能实现第二个目的(处理异常)。

如果要处理异常,可以改成下面这个样子。

import [email protected] open_func(file_name):  # __enter__方法  print('open file:', file_name, 'in __enter__')  file_handler = open(file_name, 'r')  try:    yield file_handler  except Exception as exc:    # deal with exception    print('the exception was thrown')  finally:    print('close file:', file_name, 'in __exit__')    file_handler.close()    returnwith open_func('/Users/MING/mytest.txt') as file_in:  for line in file_in:    1/0    print(line)

好像只要讲到上下文管理器,大多数人都会谈到打开文件这个经典的例子。

但是在实际开发中,可以使用到上下文管理器的例子也不少。我这边举个我自己的例子。

在OpenStack中,给一个虚拟机创建快照时,需要先创建一个临时文件夹,来存放这个本地快照镜像,等到本地快照镜像创建完成后,再将这个镜像上传到Glance。然后删除这个临时目录。

这段代码的主逻辑是创建快照,而创建临时目录,属于前置条件,删除临时目录,是收尾工作。

虽然代码量很少,逻辑也不复杂,但是“创建临时目录,使用完后再删除临时目录”这个功能,在一个项目中很多地方都需要用到,如果可以将这段逻辑处理写成一个工具函数作为一个上下文管理器,那代码的复用率也大大提高。

代码是这样的

总结起来,使用上下文管理器有三个好处:

  • 提高代码的复用率;
  • 提高代码的优雅度;
  • 提高代码的可读性;

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


  • 上一条:
    使用python获取邮箱邮件的设置方法
    下一条:
    Python Opencv提取图片中某种颜色组成的图形的方法
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 在python语言中Flask框架的学习及简单功能示例(0个评论)
    • 在Python语言中实现GUI全屏倒计时代码示例(0个评论)
    • Python + zipfile库实现zip文件解压自动化脚本示例(0个评论)
    • python爬虫BeautifulSoup快速抓取网站图片(1个评论)
    • vscode 配置 python3开发环境的方法(0个评论)
    • 近期文章
    • 智能合约Solidity学习CryptoZombie第四课:僵尸作战系统(0个评论)
    • 智能合约Solidity学习CryptoZombie第三课:组建僵尸军队(高级Solidity理论)(0个评论)
    • 智能合约Solidity学习CryptoZombie第二课:让你的僵尸猎食(0个评论)
    • 智能合约Solidity学习CryptoZombie第一课:生成一只你的僵尸(0个评论)
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2018-04
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2022-01
    • 2023-07
    • 2023-10
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客