上下文管理器之《with操作文件为什么会自动关闭?》

2021-10-13 11:18:26 浏览数 (1)

一、with操作文件为什么会自动关闭?

它的底层是通过上下文管理器实现的。

代码语言:javascript复制
with open('test.txt','w',encoding='utf8')as f:
    f.write("国庆也别忘了学习啊")

# with后面跟的是一个上下文管理器对象
    

运行后自动生成:

通过with打开一个文件,默认编码是jdK形式写的,所以加上encoding指定编码为utf8。

f是文字操作的句柄,通过这个句柄就可以往里面写东西。或者已读模式打开,可以往里面读东西,读完之后,这个文件不用关闭。怎么实现的呢?

with关键字后面跟的是个对象,with关键字会触发后面这个对象open('test.txt','w',encoding='utf8')

不是with是上下文管理器,是with后面这个open('test.txt','w',encoding='utf8')对象是上下文管理器。

通过with处理后面这个上下文管理器的时候,它会触发上下文管理器里面的某个方法。

二、什么是上下文管理器?上下文管理器又是怎么实现的呢?

上下文管理器是一个Python对象,为操作提供了额外的上下文信息。这种额外的信息,在使用with语句初始化上下文,以及完成with块中的所有代码时,采用可调用的形式。

实现上下文管理器的话,只需要自己定义个类去实现。只需要在类里面实现2个方法,一个叫做enter方法,一个叫做exit方法。

1.enter方法是干嘛的?

当我们使用with去处理一个上下文管理器对象的时候,首先会调用这个对象里面的enter方法,然后将enter方法返回的内容返回到as f

这个f就相当于上下文管理器里面enter方法返回的一个对象(返回的一条数据)。它返回什么,f就是什么。

2.exit方法是干嘛的?

打开一个上下文管理器,当with里面的代码执行完了以后,那么就会触发上下文管理器里面的另外一个exit方法退出。

这个方法默认有4个参数:

object.__exit__(self,exc_type,exc_val,exc_tb)

exc_type:#异常类型

exc_val:#异常值

exc_tb:#异常回溯追踪

它会把异常数据存储起来,在这个上下文管理器内部发生异常的时候,它能够自动将你异常的信息捕获到,传递到这个方法里面来。

open是python内置实现的一个上下文管理器。

三、自己手写一个操作文件的上下文管理器

上下文管理器就是个对象,先定义个类,继承object。实现一个enter方法,这个方法返回的内容就是f。

要实现上下文管理器必须两个方法,还有个方法是exit。这个方法默认有4个参数,不要去动它。

操作文件的时候,这里有3个参数open('test.txt','w',encoding='utf8'),其中encoding是个默认参数,可传可不传。

前面2个参数要传进来,open是个上下文管理器,其实也是个类。我们要操作文件,也得传文件名称和参数。

with MyOpen("text.txt","r")这里参数会传到哪里去?

通过类创建对象的时候,参数会传到init方法里面去。

实现一个init方法,不然传进去的参数接收不了。

定义一个init方法来接收。file_name是文件名,打开的一个方法是open_method。

这个时候传进来的

代码语言:javascript复制

with MyOpen("text.txt","r")as f:
    pass

"text.txt","r"这两个参数就有地方接收了。

接收完毕后就要打开文件,打开文件返回f,f是哪个地方返回出来的?

return "python"这个地方返回出来的。

打印一下,看下f是个什么?

代码语言:javascript复制
# with open('test.txt','w',encoding='utf8')as f:
#     f.write("国庆也别忘了学习啊")

# with后面跟的是一个上下文管理器对象

class  MyOpen(object):
    # 文件操作的上下文管理器类
    def __init__(self,file_name,open_method):
        self.file_name=file_name
        #做个初始化
        self.open_method=open_method
    def __enter__(self):
        return "python"
    def __exit__(self, exc_type, exc_val, exc_tb):
        pass

with MyOpen("test.txt","r")as f:
    pass
    print(f)

结果是:

with操作文件的时候其实就是对普通打开文件的一个封装。

上面第一个open的上下文管理器返回的是可操作文件的句柄,我也想返回一个可操作文件的句柄,可以这样修改代码:

代码语言:javascript复制
# with open('test.txt','w',encoding='utf8')as f:
#     f.write("国庆也别忘了学习啊")

# with后面跟的是一个上下文管理器对象

class  MyOpen(object):
        # 文件操作的上下文管理器类
    def __init__(self,file_name,open_method):
        self.file_name=file_name#做个初始化
        self.open_method=open_method

    def __enter__(self):
        self.f=open(self.file_name,self.open_method) #普通打开文件的方式
        return self.f#如果不写self,要关闭文件,下面exit方法够不到

    def __exit__(self, exc_type, exc_val, exc_tb):
        pass

with MyOpen('test.txt','r') as f:
    print(f)

重点是这段代码:

代码语言:javascript复制
self.f=open(self.file_name,self.open_method) #普通打开文件的方式
return self.f#如果不写self,要关闭文件,下面exit方法够不到

运行结果中有个cp936,windows中cp936代表文件打开方式是gbk。

0 人点赞