什么是上下文管理器

上下文装饰器确保函数可以运行在正确的上下文中,或者是函数前后运行一些代码,上下文装饰器通常会被上下文管理器(with)替代。
为了确保即使出现错误的情况也能运行某些清理代码,try…finally语句是很有用的,例如以下使用场景:
关闭一个文件
释放一个锁
创建一个临时的代码补丁

工作原理

任何实现了上下文管理器协议的对象都可以用做上下文管理器,协议包含两个特殊方法:
_enter(self):
_exit(self, exc_type, exc_value, traceback):

with语句执行过程如下:
调用enter方法,任何返回值都会绑定到指定的as子句
执行内部代码块
调用exit方法

  1. class ContextIlustrations:
  2. def __enter__(self):
  3. print("entering context")
  4. def __exit__(self, exc_type, exc_val, exc_tb):
  5. print("leaving coontext")
  6. if exc_type is None:
  7. print("with no error")
  8. else:
  9. print("with an error {}".format(exc_val))
  10. if __name__ == '__main__':
  11. with ContextIlustrations():
  12. print("inside")
  13. ------------------------ --------------
  14. #entering context
  15. #inside
  16. #leaving coontext
  17. #with no error
  18. class context:
  19. def __init__(self, filename):
  20. self.filename = filename
  21. def __enter__(self):
  22. print("entering context!!!!")
  23. self.f = open(self.filename, 'r')
  24. return self.f
  25. def __exit__(self, exc_type, exc_val, exc_tb):
  26. print("leaving context")
  27. self.f.close()
  28. test = context('get_dept_id.txt')
  29. with test as t:
  30. print("test resutl: {}".format(t))
  31. ------------------------
  32. #entering context!!!!
  33. #test resutl: <_io.TextIOWrapper name='get_dept_id.txt' mode='r' encoding='cp936'>
  34. #leaving context

实现通用语法

  1. with context_manager:
  2. # 如果提供了上下文变量,可以用as子句保存为临时变量:
  3. with context_magager as context:
  4. # 如果多个上下文管理器同时使用:
  5. with A() as a, B() as b:
  6. ...
  7. #这种写法等同于:
  8. with A() as a:
  9. with B() as b:
  10. ...

通过上下文管理器处理异常

  1. class Resource():
  2. def __enter__(self):
  3. print('===connect to resource===')
  4. return self
  5. def __exit__(self, exc_type, exc_type, exc_tb):
  6. """
  7. exc_type: 异常类型
  8. exc_type: 异常值
  9. exc_tb: 异常的错误栈信息
  10. """
  11. # 异常可以在 __exit__ 进行捕获并由你自己决定如何处理,
  12. # 是抛出呢还是在这里就解决了。在 __exit__ 里返回 True (
  13. # 没有return 就默认为 return False),就相当于告诉 Python解释器,这个异常我们已经捕获了,不需要再往外抛了。
  14. print('===close resource connection===')
  15. return True
  16. def operate(self):
  17. 1/0
  18. with Resource() as res:
  19. res.operate()
  20. # ===connect to resource===
  21. # ===close resource connection===

contextlib协议实现上下文管理器-管理资源

  1. 被装饰函数必须是一个生成器(有yield)
  2. yield之前的代码相当于 enter
  3. yield之后的代码相当于 exit
  1. import contextlib
  2. @contextlib.contextmanager
  3. def open_func(file_name):
  4. """能实现上下文管理器的资源管理"""
  5. # __enter__方法
  6. print("open file: ", file_name, "in __enter__")
  7. file_handler = open(file_name, "r")
  8. yield file_handler
  9. # __exit__ 方法
  10. print("close file: ", file_name, "in __exit__")
  11. file_handler.close()
  12. with open_func("test1.py") as f:
  13. for line in f:
  14. print(line)

contextlib协议实现上下文管理器-管理资源+异常处理

  1. import contextlib
  2. @contextlib.contextmanager
  3. def open_func1(file_name):
  4. """能实现上下文管理器的第一个目的(管理资源) + 处理异常"""
  5. print("open file: ", file_name, "in __enter__")
  6. file_handler = open(file_name, "r")
  7. try:
  8. yield file_handler
  9. except BaseException as exc:
  10. print("the exception was: ", exc)
  11. finally:
  12. print("close file: ", file_name, "in __exit__")
  13. file_handler.close()
  14. return
  15. with open_func1("test1.py") as f:
  16. for line in f:
  17. 1/0
  18. print(line)
  19. # open file: test1.py in __enter__
  20. # the exception was: division by zero
  21. # close file: test1.py in __exit__