上下文管理器 是一个包装任意代码块的对象。上下文管理器保证进入上下文管理器时,每次代码执行的一致性;当退出上下文管理器时,相关资源会被正确收回。
-
关键字
with
with open("123.txt", "r")as f: contents = f.read() class File: def __init__(self, file): self.file = file # 表示进入 def __enter__(self): self.open_file = open(self.file, "r", encoding="utf-8") return self.open_file # 表示退出 def __exit__(self, exc_type, exc_val, exc_tb): self.open_file.close() return True with File("123.txt") as file: print(file.read())
with
语句的表达式的作用是返回一个遵循特定协议的对象,该对象必须定义一个__enter__
和__exit__
方法,且后者必须接受特定的参数
class ContextManager: def __init__(self): self.entered = False def __enter__(self): self.entered = True return self # exc_type异常类型 exc_val异常实例 exc_tb回溯 def __exit__(self, exc_type, exc_val, exc_tb): self.entered = False con = ContextManager() print(con.entered) with con: # 处于进入状态 print(con.entered) with ContextManager() as cm: print(cm.entered)
-
适合编写上下文管理器
# 资源清理 import psycopg2 # postgreSQL class DBConnection: def __init__(self, dbname=None, user=None, password='5841', host='127.0.0.1'): self.host = host self.dbname = dbname self.user = user self.password = password def __enter__(self): self.connection = psycopg2.connect( dbname = self.dbname, host = self.host, user = self.user, password = self.password ) return self.connection.cursor() def __exit__(self, exc_type, exc_val, exc_tb): self.connection.close() # # with DBConnection(user='luke', dbname='foo')as db: # db.execute('select 1 + 1') # print(db.fetchall())
# 避免重复 class BubbleExceptions: def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): if exc_val: print("Bubbling up exception: %s."%exc_val) return False # 返回值会给exc_tb 如果异常为假则重新抛出异常 # return True 异常为真时,不需抛出异常 回溯消失 with BubbleExceptions(): print(5/0) with BubbleExceptions(): try: print(5/0) # 如果__exit__ 返回值为真时,不在执行下面语句 except ZeroDivisionError as e: print(e)
# 处理异常类 class HandleValuesError: 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) return True return False with HandleValuesError(): raise ValueError("wrong value.")
# 不包括的子类 class ValuesErrorSubclass(ValueError): pass class HandleValuesError: def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): if not exc_type: return True # print(issubclass(ValueError, ValueError)) # True if exc_type == ValueError: print("Handling ValueError:%s"%exc_val) return True return False with HandleValuesError(): raise ValuesErrorSubclass("foo and baz")
# https://www.runoob.com/w3cnote/python3-subprocess.html # 基于属性的异常处理 import subprocess # 进程处理模块 class ShellException(Exception): def __init__(self, code, stdout="", stderr=""): self.code = code self.stdout = stdout self.stderr = stderr def __str__(self): return 'exit code %d - %s'%(self.code, self.stderr) class AcceptableErrorCodes: def __init__(self, *error_codes): self.error_codes = error_codes def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): if not exc_type: return True if not issubclass(exc_type, ShellException): return False return exc_val.code in self.error_codes def run_command(command): proc = subprocess.Popen(command.split(' '), stdout=subprocess.PIPE, stderr=subprocess.PIPE) proc.wait() stdout, stderr = proc.communicate() if proc.returncode > 0: raise ShellException(proc.returncode, stdout, stderr) return stdout with AcceptableErrorCodes(): print(run_command("pip -list"))
-
更简单的语法
import contextlib import subprocess # 进程处理模块 class ShellException(Exception): def __init__(self, code, stdout="", stderr=""): self.code = code self.stdout = stdout self.stderr = stderr def __str__(self): return 'exit code %d - %s'%(self.code, self.stderr) ''' @contextlib.contextmanager 会将acceptable_error_codes转换成一个上下文管理器类 该装饰器的函数在函数执行期间返回单个值 ''' @contextlib.contextmanager def acceptable_error_codes(*codes): try: yield except ShellException as exc_val: if exc_val.code not in codes: raise pass def run_command(command): proc = subprocess.Popen(command.split(' '), stdout=subprocess.PIPE, stderr=subprocess.PIPE) proc.wait() stdout, stderr = proc.communicate() if proc.returncode > 0: raise ShellException(proc.returncode, stdout, stderr) return stdout with acceptable_error_codes(1): print(run_command("rm 123.txt"))