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

前言

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.contextmanager
def 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 contextlib
@contextlib.contextmanager
def 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()
    return
with open_func('/Users/MING/mytest.txt') as file_in:
  for line in file_in:
    1/0
    print(line)

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

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

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

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

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

代码是这样的

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

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

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

(0)

相关推荐

  • Python中的上下文管理器和with语句的使用

    Python2.5之后引入了上下文管理器(context manager),算是Python的黑魔法之一,它用于规定某个对象的使用范围.本文是针对于该功能的思考总结. 为什么需要上下文管理器? 首先,需要思索下为什么需要引入上下文管理器. 在正常情况下,管理各种系统资源(如文件).数据库连接时,通常是先打开这些资源,执行完相应的业务逻辑,最后关闭资源. 举两个例子: 1.使用Python打开一个文件写入内容,之后需要关闭这个文件.如果不正常关闭的话可能会在文件操作时出现异常,因为系统允许你打开的

  • Python标准模块--ContextManager上下文管理器的具体用法

    写代码时,我们希望把一些操作放到一个代码块中,这样在代码块中执行时就可以保持在某种运行状态,而当离开该代码块时就执行另一个操作,结束当前状态:所以,简单来说,上下文管理器的目的就是规定对象的使用范围,如果超出范围就采取"处理". 这一功能是在Python2.5之后引进的,它的优势在于可以使得你的代码更具可读性,且不容易出错. 1 模块简介 在数年前,Python 2.5 加入了一个非常特殊的关键字,就是with.with语句允许开发者创建上下文管理器.什么是上下文管理器?上下文管理器就

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

    1. 上下文管理器是什么? 举个例子,你在写Python代码的时候经常将一系列操作放在一个语句块中: (1)当某条件为真 – 执行这个语句块 (2)当某条件为真 – 循环执行这个语句块 有时候我们需要在当程序在语句块中运行时保持某种状态,并且在离开语句块后结束这种状态. 所以,事实上上下文管理器的任务是 – 代码块执行前准备,代码块执行后收拾. 上下文管理器是在Python2.5加入的功能,它能够让你的代码可读性更强并且错误更少.接下来,让我们来看看该如何使用. 2. 如何使用上下文管理器? 看

  • Python深入学习之上下文管理器

    上下文管理器(context manager)是Python2.5开始支持的一种语法,用于规定某个对象的使用范围.一旦进入或者离开该使用范围,会有特殊操作被调用 (比如为对象分配或者释放内存).它的语法形式是with...as... 关闭文件 我们会进行这样的操作:打开文件,读写,关闭文件.程序员经常会忘记关闭文件.上下文管理器可以在不需要文件的时候,自动关闭文件. 下面我们看一下两段程序: 复制代码 代码如下: # without context manager f = open("new.t

  • Python with语句上下文管理器两种实现方法分析

    本文实例讲述了Python with语句上下文管理器.分享给大家供大家参考,具体如下: 在编程中会经常碰到这种情况:有一个特殊的语句块,在执行这个语句块之前需要先执行一些准备动作:当语句块执行完成后,需要继续执行一些收尾动作.例如,文件读写后需要关闭,数据库读写完毕需要关闭连接,资源的加锁和解锁等情况. 对于这种情况python提供了上下文管理器(Context Manager)的概念,可以通过上下文管理器来定义/控制代码块执行前的准备动作,以及执行后的收尾动作. 一.为何使用上下文管理器 1.

  • Python中的with语句与上下文管理器学习总结

    0.关于上下文管理器 上下文管理器是可以在with语句中使用,拥有__enter__和__exit__方法的对象. with manager as var: do_something(var) 相当于以下情况的简化: var = manager.__enter__() try: do_something(var) finally: manager.__exit__() 换言之,PEP 343中定义的上下文管理器协议允许将无聊的try...except...finally结构抽象到一个单独的类中,

  • 正确理解python中的关键字“with”与上下文管理器

    前言 如果你有阅读源码的习惯,可能会看到一些优秀的代码经常出现带有 "with" 关键字的语句,它通常用在什么场景呢?今天就来说说 with 和 上下文管理器. 对于系统资源如文件.数据库连接.socket 而言,应用程序打开这些资源并执行完业务逻辑之后,必须做的一件事就是要关闭(断开)该资源. 比如 Python 程序打开一个文件,往文件中写内容,写完之后,就要关闭该文件,否则会出现什么情况呢?极端情况下会出现 "Too many open files" 的错误,

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

    前言 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. 上下文管理器:o

  • 深入学习Python中的上下文管理器与else块

    前言 本文主要个大家介绍了关于Python上下文管理器与else块的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 在开始之前,我们先来看看下面这段话: 最终,上下文管理器可能几乎与子程序(subroutine)本身一样重要.目前,我们只了解了上下文管理器的皮毛--Basic 语言有with 语句,而且很多语言都有.但是,在各种语言中 with 语句的作用不同,而且做的都是简单的事,虽然可以避免不断使用点号查找属性,但是不会做事前准备和事后清理.不要觉得名字一样,就意

  • Python上下文管理器全实例详解

    Python上下文管理器 简介 最近用到这个,仔细了解了一下,感觉是十分有用的,记录一下 使用场景 当我们需要获取一个临时打开的资源,并在使用完毕后进行资源释放和异常处理,利用try-catch语句可以完成,举个例子. 打开文件: f = None try: print("try") f = open("__init__.py", "r") print(f.read()) except Exception as e: print("ex

  • python框架django项目部署相关知识详解

    这篇文章主要介绍了python框架django项目部署相关知识详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一:项目部署的框架 nginx和uWSGI在生产服务器上进行的部署 二:什么是nginx? nginx是一个web服务器. 什么是web服务器? web服务器则主要是让客户可以通过浏览器进行访问,处理HTML文件,css文件,js文件,图片等资源.web服务器一般要处理静态文件.对接服务器. 什么是静态文件? css,js,html

  • 对python中if语句的真假判断实例详解

    说明 在python中,if作为条件语句,当if后面的条件参数为真时,则执行后面的语句块,反之跳过,为了深入理解if语句,我们需要知道if语句的真假判断方式. 示例 在python交互器中,经过测试发现以下条件均为假,相当于False In [2]: if '': ...: print('ok') ...: In [3]: if 0: ...: print('ok') ...: In [4]: if None: ...: print('ok') ...: In [5]: if []: ...:

  • python中前缀运算符 *和 **的用法示例详解

    这篇主要探讨 ** 和 * 前缀运算符,**在变量之前使用的*and **运算符. 一个星(*):表示接收的参数作为元组来处理 两个星(**):表示接收的参数作为字典来处理 简单示例: >>> numbers = [2, 1, 3, 4, 7] >>> more_numbers = [*numbers, 11, 18] >>> print(*more_numbers, sep=', ') 2, 1, 3, 4, 7, 11, 18 用途: 使用 * 和

  • Python 中 Virtualenv 和 pip 的简单用法详解

    本文介绍了Python 中 Virtualenv 和 pip 的简单用法详解,分享给大家,具体如下: 0X00 安装环境 我们在 Python 开发和学习过程中需要用到各种库,然后在各个不同的项目和作品里可能用的版本还不一样,正因为有这种问题的存在才催生了virtualenv的诞生.virtualenv 可以在电脑上创建一个虚拟环境,可以针对每一个项目创建一个虚拟环境,这样就不用担心各个不同的项目用不同版本的库的时候出现的冲突了. 下面的内容只适用于 Linux/OSX,未经 Windows 环

随机推荐

其他