Python中有一个非常神奇的关键字叫with,打开文件用完之后我们要记得关闭,若我们的代码在运行过程中发生了异常,导致关闭文件的代码没有被执行到,肿么办,有小伙伴会说我们有 try..finally块啊,有没有更简单的方便的方案:
有就是with语句,它作为try/finally的替代方案,非常方便
要点:
1)with的基本用法
2)with的执行过程
3)深入剖析with内部原理
4)实战证明with的神奇
1.基本用法:
with context as var:
with_suite
- context 是一个表达式会返回一个对象,返回的对象会赋值给var
- with_suite使用var变量来对context返回的对象进行操作
我们先看一个简单的例子:
假如当前目录下有一个123.txt文件,里面的内容是100
|---123.txt---
100
一般的用法是:
f = open("123.txt")
try:
data = f.read()
print int(data)
finally:
print 'file close'
f.close()
用了with之后,代码就瘦身成这样(减肥神器啊):
with open('123.txt') as f:
line=f.read()
print line
>>
100
2.with的执行过程
执行过程是这样的:
1).open打开了'123.txt'这个文件
2).返回文件句柄并赋值给了f,f执行read()函数,并打印读出的结果
3).with的代码执行完了,f就会被自动关闭
有同学肯定忍不住的问,你还是没有解释为啥with语句能自动关闭啊,好我们来剖析一下:
3.深入剖析with内部原理,4步搞定
step1:
1).with 语句看起来简单,其实是因为有一个上下文管理器的大神在背后默默的奉献,
- 计算context的值,返回一个对象,这个对象是一定要支持上下文管理器的对象上下文管理器这个大神,一定要包含__enter__()和__exit__()方法
- 进入上下文管理器:调用上下文管理器的__enter__()方法,返回运行时的上下文相关的对象,with语句会把这个返回值绑定到目标对象,也就是获得文件句柄,并把这个句柄赋值给f
- 执行with中的代码
- 如果with中的代码顺利执行完毕,调用上下文管理器里的__exit()__方法,其返回值直接忽略
注意重点来了,重点来了,重点来了
-
- 若在with中的代码执行过程中,呱唧发生了异常,也会调用上下文管理器的__exit__()方法,并把异常的类型,异常的值和traceback信息全部都以参数的形式传递给__exit__()方法
- __exit__(exception_type,exception_value,traceback),这个方法可以处理异常,清理现场,处理with块语句执行完要处理的动作
这也就是为啥with神奇之处,其实就是引入一个上下文管理器,定义了程序运行是需要建立的上下文, 处理程序的进入和退出.就这么简单
step2:
2).讲到这个小伙伴也会明白了一些,但是还是有一丝迷雾不解,__enter()__和__exit__()是不是真的跟你说的那样呢~~
我们继续看一下刚才那个'123.txt'文件
f=open('123.txt')
print f.__enter__
print f.__exit__
>>
<built-in method __enter__ of file object at 0x000000000AAEEF60>
<built-in method __exit__ of file object at 0x000000000AAEEF60>
果然__enter()__和__exit__()都是file内置方法
step3:
3).好我们把代码改一下,调用__enter__(),read()和__exit()__函数看看
f=open('123.txt')
var=f.__enter__()
print var
print f.read()
f.__exit__()
>>
<open file '123.txt', mode 'r' at 0x000000000721CAE0>
100
- 确实__enter__()返回了打开文件的句柄,然后我们读取文件的值,打印出100,然后文件游标运行到了文件结尾
- 调用__exit()__关闭文件
那么我们怎么证明f.__exit__()已经把文件关闭了呢
step4:
4).继续把刚才的代码修改,再加一行
f=open('123.txt')
var=f.__enter__()
print var
print f.read()
f.__exit__()
print f.read()#再读一遍文件
>>
<open file '123.txt', mode 'r' at 0x000000000AAEEF60>
100
print f.read()
ValueError: I/O operation on closed file
果然文件已经关闭了,所以再读的时候会报错.
4.实战证明with的神奇
1).回到最开始的例子,我们也可以证明一下文件是否关闭:
with open('123.txt') as f:
line=f.read()
print line
print 'flie close status:',f.closed#调用文件的closed属性
>>
100
flie close status: True
2)如果我们把'123.txt'文件里面的内容改成aa,看看文件是否被自动关闭了:
|---123.txt---
aa
with open('123.txt') as f:
line=f.read()
print int(line)
print 'flie close status:',f.closed
>>
ValueError: invalid literal for int() with base 10: 'aa'
注意因为在with语句内执行int(line)出现了ValueError,而这个我们没有定义异常处理的代码,所以直接抛出到最顶层,也就是让Python解释器处理,若我们加上异常处理代码,看看会发生什么~~
3)增加try/except代码来证明一下:
try:
with open('123.txt') as f:
line=f.read()
print int(line)
except ValueError,e:
print 'catch the error:',e
print f
print 'flie close status:',f.closed
>>
catch the error: invalid literal for int() with base 10: 'aa'
<closed file '123.txt', mode 'r' at 0x020AA230>
flie close status: True
说明文件其实已经close了,只是因为没有异常处理块,无法打印,所以我们看不到文件的关闭状态
结论:文件处理用with,无论程序以何种方式跳出with块,总能保证文件被正确关闭,其实不仅仅对文件,对线程锁的管理也同样适用.
留个小问题大家讨论:
1)如果在最开始open文件的时候出错了,with还能自动的帮我们关闭文件吗
2)你认为代码1和代码2是等效的,还是代码1和代码3等效的
答案我会在下期的文章里面详细解答一下
代码1:
with open('1234.txt')as f:
data=f.read()
print data
代码2:
f = open('1234.txt')
try:
data = f.read()
print data
finally:
f.close()
代码3:
try:
f = open('1234.txt')
data = f.read()
print data
finally:
f.close()
好了深入剖析Python中的异常处理神器-with就讲到这里啦,希望能给初学者一些启发,若有什么不懂的,也可以留言跟我探讨交流.
本文为原创文章,版权归知行编程网所有,欢迎分享本文,转载请保留出处!
内容反馈