python 上下文管理器的实现原理-学习过程
学习廖雪峰老师课程,并根据我最近查阅的资料并整理和理解的过程,后期我会调整文档格式
#实现上下文管理器实现上下文管理器有两种方式实现,
#方法一:实现__enter__和__exit__方法
1 class Query(object): 2 3 def __init__(self,name): 4 self.name=name 5 6 def __enter__(self): 7 print('开始') 8 return self #返回值绑定在as后面的变量上 9 10 def __exit__(self, type, value, traceback): 11 if type: #第一个参数返还的是true或者false 12 print('错误') 13 else: 14 print('结束') 15 def query(self): 16 print('Query信息是 %s...' % self.name) 17 with Query('Bob') as q: 18 19 q.query()
#A.运行顺序 with语句先暂存了Query类的__exit__方法,然后它调用Query类的__enter__方法。
#__enter__方法打开文件并返回给with语句,打开的文件句柄被传递给q(就是with 的VAR)参数。
#with语句调用之前暂存的__exit__方法,__exit__方法关闭了文件。
#with EXPR as VAR:
# BLOCK
#B.异常处理
#关于异常处理,with语句会采取哪些步骤。
#1. 它把异常的type,value和traceback传递给__exit__方法
#2. 它让__exit__方法来处理异常
#3. 如果__exit__返回的是True,那么这个异常就被忽略。
#4. 如果__exit__返回的是True以外的任何东西,那么这个异常将被with语句抛出。
class File(object): def __init__(self, file_name, method): self.file_obj = open(file_name, method) #注意open放在初始化实例种 def __enter__(self): return self.file_obj #返回值绑定在as后面的变量上 def __exit__(self, type, value, traceback): self.file_obj.close() print('==================') print('type-->%s value-->%s traceback-->%s'%(type, value, traceback)) return True #异常忽略,__exit__返回的是True,那么这个异常就被忽略。 with File('./123.txt','w') as opened: opened.undefined_function('hobby') #错误错误,这一步不会执行其他不影响
#我习惯的写法和上面的区别,更加简洁明了
class File(object): def __init__(self, file_name, method): self.file_name=file_name self.method=method def __enter__(self): print('进入') self.x=open(self.file_name,self.method) return self.x #返回值绑定在as后面的变量上 def __exit__(self, type, value, traceback): #3个数据是为错误提供返回值分别为异常类型、异常信息和堆栈 print('退出') self.x.close() return True with File('./123.txt', 'w') as x: #x是调用__enter__返回值 print('Running') x.write('Hola!')
#=======================contextlib==================================
#yield之前的代码由__enter__方法执行,yield之后的代码由__exit__方法执行。
# 本质上还是__enter__和__exit__方法。
from contextlib import contextmanager @contextmanager def myopen(filename, mode): f = open(filename, mode) yield f.readlines() f.close() if __name__ == '__main__': with myopen('./123.txt', 'r') as f: for line in f: print(line)
#=================with语句上多个下文关联============================
直接通过一个with语句打开多个上下文,即可同时使用多个上下文变量,而不必需嵌套使用with语句。
class File(object): def __init__(self, file_name, method): self.file_obj = open(file_name, method) def __enter__(self): return self.file_obj def __exit__(self, exception_type, exception_value, traceback): self.file_obj.close() return True with File('./456.txt', 'w') as f1,File('./4561.txt','w') as f2: print (f1,f2)
#======================contextlib==================================
from contextlib import contextmanager class Query(object): def __init__(self,name): self.name=name def query(self): print('函数传入参数是->',self.name) @contextmanager #装饰器-- def create_query(name): print('开始') q=Query(name) yield q print('结束') with create_query('Bob') as q: q.query()
我们希望在某段代码执行前后自动执行特定代码,也可以用@contextmanager实现。例如:
from contextlib import contextmanager @contextmanager def tag(name): print('<{}>'.format(name)) yield print('<%s>'%name) with tag('h1'): print('hello') print('hobby')
#详解
#代码的执行顺序是:
#with语句首先执行yield之前的语句,因此打印出<h1>;
#yield调用会执行with语句内部的所有语句,因此打印出hello和world;
#最后执行yield之后的语句,打印出</h1>。
#=======================别人的列子====================================
from contextlib import contextmanager class Query(): #定义查询器 #保存一个人名,供示例中的函数查询 def __init__(self,name): self.name=name #查询,与之前保存的人名对比,若不是同一个人名 #自定义返回错误类型的query函数。 def query(self,xname): print('-->Query:{0}'.format(xname)) if self.name==xname: print('正确{self.name}') else: raise BaseException('error-->{}'.format(xname)) #定义一个上下文管理函数,作用是简单的打印出调用对象中的错误。 @contextmanager def oder(sr): try: sr=Query(sr) #这一步可放入with中进行(放入此处可读性较高)yield==return且有分割作用, yield sr except NameError as a: print(a) with oder('hobby')as x: #保存一个人名,供示例中的函数查询 x.query('laowang')