一、with语句
为什么需要with 语句,
1. 若你打开了一个文件,可使用完这个文件后却忘却了关闭,那么整个文件可能还在内存中,造成资源浪费;
2. 若你以只读的方式打开一个文件,却往文件里面写点东西,这时候程序会奔溃,这时候文件又未正常关闭;
with 语句很好解决了上诉的烦恼, with语句 简单又安全,不管正常结束或者出现异常 都会自动关闭该文件
# 1、以追加的方式打开文件
with open("test.txt", "a") as f:
# 2、读取文件内容
f.write("hello world\n")
# 1、以读的方式打开文件,文件也会关闭
with open("test.txt", "r") as f:
# 2、读取文件内容
try:
f.write("hello world\n")
except:
print('file not writable')
print('----end-----')
#file not writable
#----end-----
二、上下文管理器
为什么文件能使用with 语句,那是因为文件实现了__enter__()函数,__exit__()函数。
方法__enter__:不接受任何参数,在进入with语句时被调用,其返回值被赋给关键字as后面的变量。
方法__exit__: 接受三个参数, 异常类型、异常对象和异常跟踪。它在离开方法时被调用。如果__exit__返回False,将向上抛出所有的异常。
class FileManage(object):
# 初始化方法
def __init__(self, file_name, file_model):
# 定义变量保存文件名和打开模式
self.file_name = file_name
self.file_model = file_model
# 上文方法
def __enter__(self):
print("进入上文方法")
# 返回文件资源
self.file = open(self.file_name,self.file_model)
return self
# 下文方法
def __exit__(self, exc_type, exc_val, exc_tb):
print("进入下文方法")
print(f'exc_type:{exc_type}')
print(f'exc_val:{exc_type}')
print(f'exc_tb:{exc_tb}')
self.file.close()
print("文件已经关闭")
return True
def writesomething(self, text):
self.file.write(text)
if __name__ == '__main__':
# 使用with管理文件
with FileManage("test.txt", "r") as f:
f.writesomething('hello,world\n')
print('----end-----')
输出:
进入上文方法
进入下文方法
exc_type:<class 'io.UnsupportedOperation'>
exc_val:<class 'io.UnsupportedOperation'>
exc_tb:<traceback object at 0x0000018EBC683648>
文件已经关闭
----end-----
以传入文件名和打开方式, 进入了__enter__ 函数,打开文件 并返回 类本身实例, 但是却往里面写东西,此时出现出现异常,进入收尾工作__exit__, 打印出异常的类型。
如果__exit__ 返回值为False ,那么就会继续向上抛出异常,程序奔溃
进入上文方法
进入下文方法
exc_type:<class 'io.UnsupportedOperation'>
exc_val:<class 'io.UnsupportedOperation'>
exc_tb:<traceback object at 0x000002585C313688>
文件已经关闭
Traceback (most recent call last):
File "F:/PythonPro/main.py", line 31, in <module>
f.writesomething('hello,world\n')
File "F:/PythonPro/main.py", line 25, in writesomething
self.file.write(text)
io.UnsupportedOperation: not writable
三、上下文管理器装饰器 @contextmanager 实现
一个函数若想成为上下文管理器,使用python提供的 @contextmanager 的装饰器,简化了上下文管理器的实现方式。
通过 yield 将函数分割成两部分:
yield 上面的语句在 __enter__ 方法中执行;
yield 下面的语句在 __exit__ 方法中执行;
紧跟在 yield 后面的参数是函数的返回值。
# 导入装饰器
from contextlib import contextmanager
# 装饰器装饰函数,让其称为一个上下文管理器对象
@contextmanager
def myopenfile(file_name, file_mode):
try:
# 打开文件
file = open(file_name, file_mode)
# 1. yield之前的代码 是__enter__方法中执行的
yield file # 3.yield 后面的参数是函数的返回值
# 2. yield 下面的语句在 __exit__ 方法中执行
except Exception as e:
print(e)
finally:
print("-----end----")
file.close()
# 使用with语句
with myopefile('out.txt', 'r') as f:
f.write("hello , the simplest context manager")