上下文管理器
[TOC]
第二章 上下文管理器
2.1 上下文管理器的定义
上下文管理器是一个包装任意代码块的对象
上下文管理器保证进入上下文管理器时, 每次代码执行的一致性
当退出上下文管理器时, 相关的资源会被正确回收
上下文管理器保证退出步骤的执行
2.1.1 与装饰器的异同
与装饰器类似, 它们都是包装其他代码的工具. 装饰器包装用于定义的代码块(函数/类), 上下文管理器可以包装任意格式的代码块.
2.1.2 用途
作为确保资源被正确清理的一种方式
2.2 语法
2.2.1 with
语句
with
语句使用with
语句可以进入上下文管理器.
两段功能相同的代码
# 代码段一
try:
my_file = open('filename', 'r')
contents = my_file.read()
finally:
my_file.close()
# 代码段二
with open('filename', 'r') as my_file:
contents = my_file.read()
前提:
open()
支持with
语法实际上是
with
语句对其后代码进行求值(在代码段二中, 就是调用open
函数). 该表达式会返回一个对象, 该对象包含两个特殊方法:__enter__
和__exit__
.__enter__
方法返回的结果被复制给as
关键字之后的变量使用上下文管理器重要原因是避免代码重复
2.2.2 __enter__
和__exit__
方法
__enter__
和__exit__
方法__enter__方法
__enter__
方法只接受self
参数. 当对象返回时该方法立即执行.
__exit__方法
__exit__
方法带有3个位置参数(不包括self
):
一个异常类型
一个异常实例
一个回溯
如果没有异常, 三个参数全被设置为None
, 如果出现异常, 则参数被赋值.
简单示例
代码
# coding: utf-8
import sys
print("Python版本信息: ", sys.version)
class ContextManger(object):
def __init__(self):
self.entered = False
pass
def __enter__(self):
self.entered = True
return self
pass
def __exit__(self, exc_type, exc_val, exc_tb):
self.enabled = False
pass
cm = ContextManger()
print(cm.entered) # False
with cm:
print(cm.entered) # True
with ContextManger() as cm2:
print(cm2.entered) # True
输出
Python版本信息: 3.6.0 (v3.6.0:41df79263a11, Dec 23 2016, 08:06:12) [MSC v.1900 64 bit (AMD64)]
False
True
True
2.2.3 异常处理
上下文管理器必须定义
__exit__
方法, 该方法可以选择性地处理包装代码块中出现的异常, 或者处理其他需要关闭- 上下文管理器状态的事情.__exit__
方法必须定义三个位置参数(不包括self
参数)三种处理方法:
传播异常: 让
__exit__
方法返回False
终止异常: 让
__exit__
方法返回True
抛出不同异常
2.3 编写上下文管理器的集中情形
2.3.1 资源清理
确保出现异常时正确关闭资源可以避免很多僵尸进程.
2.3.2 异常处理
传播异常
代码
# coding: utf-8
import sys
print("Python版本信息: ", sys.version)
class BubbleExceptions(object):
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_val:
print('Bubble up exception: %s.' % exc_val)
pass
return False
pass
with BubbleExceptions():
print(5 + 5)
with BubbleExceptions():
print(5 / 0)
输出
Python版本信息: 3.6.0 (v3.6.0:41df79263a11, Dec 23 2016, 08:06:12) [MSC v.1900 64 bit (AMD64)]
10
Bubble up exception: division by zero.
Traceback (most recent call last):
File "D:/workspace/Pycharm/PurePythonProject/Test.py", line 23, in <module>
print(5 / 0)
ZeroDivisionError: division by zero
终止异常
代码
# coding: utf-8
import sys
print("Python版本信息: ", sys.version)
class BubbleExceptions(object):
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_val:
print('Suppressing exception: %s.' % exc_val)
pass
return True
pass
with BubbleExceptions():
print(5 + 5)
with BubbleExceptions():
print(5 / 0)
输出
Python版本信息: 3.6.0 (v3.6.0:41df79263a11, Dec 23 2016, 08:06:12) [MSC v.1900 64 bit (AMD64)]
10
Suppressing exception: division by zero.
注意
没有返回表达式
5 / 0
的值, 异常在计算该值的过程中引发, 从而触发__exit__
的运行.
处理特定异常
代码
# coding: utf-8
import sys
print("Python版本信息: ", sys.version)
class HandleValueError(object):
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if not exc_type:
return True
if issubclass(exc_type, ValueError):
print('Handling ValueError: %s.' % exc_val)
pass
return False
pass
with HandleValueError():
raise ValueError('Value Error')
with HandleValueError():
raise TypeError('Type Error')
输出
Python版本信息: 3.6.0 (v3.6.0:41df79263a11, Dec 23 2016, 08:06:12) [MSC v.1900 64 bit (AMD64)]
Handling ValueError: Value Error.
Traceback (most recent call last):
File "D:/workspace/Pycharm/PurePythonProject/Test.py", line 23, in <module>
raise ValueError('Value Error')
ValueError: Value Error
基于属性的异常处理
上下文管理器可以根据异常的类型决定是否处理异常, 也可以根据异常的属性来决定是否处理异常.
示例
# coding: utf-8
import sys
import subprocess
print("Python版本信息: ", sys.version)
class ShellException(Exception):
def __init__(self, code, stdout='', stderr=''):
self.code = code
self.stdout = stdout
self.stderr = stderr
pass
def __str__(self):
return 'exit code %d - %s' % (self.code, self.stderr)
pass
class AcceptableErrorCodes(object):
def __init__(self, *error_codes):
self.error_codes = error_codes
pass
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
# print(exc_val)
if not exc_type:
return True
if not issubclass(exc_type, ShellException):
return False
if exc_val.code in self.error_codes:
print('Handled exception:', exc_val)
return True
else:
print('Throw Exception:', exc_val)
return False
def run_command(command):
proc = subprocess.Popen(command.split(' '), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
proc.wait()
stdout, stderr = proc.communicate()
# print(proc.returncode)
if proc.returncode > 0:
raise ShellException(proc.returncode, str(stdout), str(stderr))
return stdout
with AcceptableErrorCodes(1):
print(run_command('rm notExistFile'))
with AcceptableErrorCodes(2):
print(run_command('rm -m notExistFile'))
输出
Python版本信息: 3.6.0 (v3.6.0:41df79263a11, Dec 23 2016, 08:06:12) [MSC v.1900 64 bit (AMD64)]
Handled exception: exit code 1 - b"rm: cannot remove 'notExistFile': No such file or directory\n"
Throw Exception: exit code 1 - b"rm: unknown option -- m\nTry 'rm --help' for more information.\n"
Traceback (most recent call last):
File "D:/workspace/Pycharm/PurePythonProject/Test.py", line 60, in <module>
print(run_command('rm -m notExistFile'))
File "D:/workspace/Pycharm/PurePythonProject/Test.py", line 52, in run_command
raise ShellException(proc.returncode, str(stdout), str(stderr))
__main__.ShellException: exit code 1 - b"rm: unknown option -- m\nTry 'rm --help' for more information.\n"
说明
根据命令执行返回代码判断是否处理异常
最后更新于
这有帮助吗?