让对象支持with语句
在测试类中实现__enter__和__exit__,并使用with语句,观察其执行顺序
class Test:
def __init__(self):
print('__init__方法')
def __enter__(self):
print('__enter__方法')
def __exit__(self, exc_type, exc_val, exc_tb):
print('__exit__方法')
with Test():
print('with体')
执行结果如下:
__init__方法
__enter__方法
with体
__exit__方法
总结:
当类实现内置方法__enter__()和__exit__(),使用with语句时,执行顺序为:
- 执行
__init__()方法 - 执行
__enter__()方法 - 执行with体
- 执行
__exit__()方法
with语句中抛异常
在例子1的基础之上,让with语句体抛异常,观察其执行顺序
with Test():
i=1/0 #抛异常
print('with体')
执行结果如下:
__init__方法
__enter__方法
__exit__方法
Traceback (most recent call last):
File "c:/Users/mym/Desktop/python/textfilereader.py", line 26, in <module>
i=1/0
ZeroDivisionError: division by zero
总结:
在with语句中抛异常时,执行顺序为:
- 执行
__init__()方法 - 执行
__enter__()方法 - 执行with体抛异常
- 抛异常后,仍然执行
__exit__()方法
忽略异常
方法__exit__()的返回值 与with语句 是否忽略异常 相关:
- 返回False:抛出异常,默认返回False
- 返回True:忽略异常
例子
改造上例代码,将方法__exit__()返回True,在with语句体抛异常,观察其执行过程:
def __exit__(self, exc_type, exc_val, exc_tb):
print('__exit__方法')
return True
with Test():
i=1/0 #抛异常
print('with体')
执行结果如下:
__init__方法
__enter__方法
__exit__方法
总结:
在with语句中抛异常时,执行顺序为:
- 执行
__init__()方法 - 执行
__enter__()方法 - 执行with体 抛异常,如果
__exit__方法 返回True,忽略异常,返回False抛出异常,如有必要,由with语句之外的语句来处理异常 - 抛异常后,仍然执行
__exit__()方法
with…as…变量
使用with ... as ...语句,观察 as变量:
with Test() as t:
print(t)
print('with体')
执行结果如下:
__init__方法
__enter__方法
None
with体
__exit__方法
总结:
在with ... as ...语句中,返回结果为None,而不是Test类对象
__enter__()返回值
在__enter__方法中返回数字1,并使用with ... as ...语句,观察 as 变量:
def __enter__(self):
print('__enter__方法')
return 1
with Test() as t:
print(t)
print('with体')
执行结果如下:
__init__方法
__enter__方法
1
with体
__exit__方法
总结:
在with ... as ...语句中,as后面变量的值,是__enter__方法的返回值,而不是Test类对象
一般__enter__方法返回self,当然具体情况具体分析
案例:读取配置文件类
在很多面向对象语言中(C++、JAVA、C#),都有读取配置文件类,(文件的“文件流”类,类名一般有file、stream、reader、writer等单词组成)
配置文件格式如下:
db_name=mysql
username=root
password=
在本例中,我们实现ConfigFileReader类,对open()方法进行封装,用于读取 配置文件 ,并且 实现上下文管理器 ,并 使用with语句 读取文件
class ConfigFileReader():
def __init__(self, filename,encoding=None):
self._file = open(filename, 'r',encoding=encoding)
def __enter__(self):
print('执行__enter__()')
return self
def __exit__(self, *args):
print('执行__exit__()')
self._file.close()
def parse(self):
ret={}
for line in self._file:
line=line.strip()
pos=line.find("=")
if pos>=0:
ret[line[:pos]]=line[pos+1:]
else:
raise Exception("[%s] 没有=,格式错误"%line)
return ret
if __name__ == "__main__":
with ConfigFileReader(r'C:\Users\mym\Desktop\python\上下文管理器\dbconfig-utf8.txt',encoding="utf-8") as f:
data=f.parse()
for key,value in data.items():
print("%s=%s"%(key,value))
总结:
- 为了实现上下文管理器,要实现__enter__()和__exit__()方法
- 在__enter__()方法中返回值赋给as语句的变量
- 在__exit__()方法执行关闭、清理等操作,并且返回True,忽略异常,返回False抛出异常
执行顺序:
- 执行
__init__()方法 - 执行
__enter__()方法 - 执行with体
- with体抛异常,仍然执行
__exit__()方法
感谢文章:
https://blog.csdn.net/xc_zhou/article/details/80810111
https://www.cnblogs.com/huchong/p/8268765.html