【python学习笔记】上下文管理器

含有__enter__和__exit__方法的对象就是上下文管理器

with 上下文管理器:

  语句体

当with遇到上下文管理器,就会在执行语句体之前,先执行上下文管理器的__enter__方法,然后在执行语句体,执行完语句体后,最后执行__exit__方法

调用上下文管理器的 __enter__ 方法时;如果使用了 as 子句,则将 __enter__() 方法的返回值赋值给 as 子句中的目标

with 上下文管理器 as target:

  代码语句体

with后面必须跟一个上下文管理器,如果使用了as,则是把上下文管理器的 __enter__() 方法的返回值赋值给 target,target 可以是单个变量,或者由“()”括起来的元组(不能是仅仅由“,”分隔的变量列表,必须加“()”)

with open("/tmp/foo.txt") as file:

  data = file.read()

  • 上下文表达式:with open("/tmp/foo.txt") as file:
  • 上下文管理器:open("/tmp/foo.txt")
  • file不是上下文管理器,应该是对应的资源对象

实例:

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===

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

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

实例:

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。

commtextlib模块

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') asfile_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)

posted @ 2021-05-11 10:57  咸鱼翻身仍是咸鱼  阅读(83)  评论(0)    收藏  举报