一、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。