瓜瓜龙

导航

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

 

 



posted on 2021-07-07 09:47  瓜瓜龙  阅读(60)  评论(1编辑  收藏  举报