一、with语句

背景:文件使用完后必须关闭,因为文件对象会占用操作系统的资源,并且操作系统同一时间能打开的文件数量也是有限的

1. 原始方式打开文件

  • 问题:文件读写时都有可能产生IOError,一旦出错,后面的f.close()就不会调用(风险写法)
  • 解决方式:添加try-except-finally语句(安全写法)
  • 缺点:代码过于冗长
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 # 1、以读的方式打开文件
f = open("1.txt", "r")
# 2、读取文件内容
f.write("hello world")
# 3、关闭文件
f.close()
try:
# 1、以读的方式打开文件
f = open("1.txt", "r")
# 2、读取文件内容
f.write("xxxxx")

except IOError as e:
print("文件操作出错", e)

finally:
# 3、关闭文件
f.close()

2. with方式打开文件

  • 优点:with 语句执行完成以后自动调用关闭文件操作,即使出现异常也会自动调用关闭文件操作
  • 原理:上下文管理器(实现__enter__和__exit__)
1
2
3
4
# 1、以写的方式打开文件
with open("1.txt", "w") as f:
# 2、读取文件内容
f.write("hello world")

二、上下文管理器的创建方式

1. 类方法实现方式

包含__enter__()__exit__()两个方法的类,通过该类创建的对象可称为上下文管理器

  • __enter__():表示上文方法,需要返回一个操作对象
  • __exit__():表示下文方法,with语句执行完成会自动执行,即使出现异常也会执行该方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 定义一个File类,实现 __enter__() 和 __exit__()方法,然后使用with语句来完成操作文件

class File(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.file

# 下文方法
def __exit__(self, exc_type, exc_val, exc_tb):
print("进入下文方法")
self.file.close()


if __name__ == '__main__':

# 使用with管理文件
with File("1.txt", "r") as file:
file_data = file.read()
print(file_data)

2. 函数装饰器实现方式

Python 还提供了一个 @contextmanager 的装饰器,更进一步简化了上下文管理器的实现方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 导入装饰器
from contextlib import contextmanager

# 装饰器装饰函数,让其称为一个上下文管理器对象
@contextmanager
def my_open(path, mode):
try:
# 打开文件
file = open(path, mode)
# yield之前的代码好比是上文方法
yield file
except Exception as e:
print(e)
finally:
print("over")
# yield下面的代码好比是下文方法
file.close()

# 使用with语句
with my_open('out.txt', 'w') as f:
f.write("hello , the simplest context manager")