深入剖析with的内部原理-异常处理的神器

浏览: 1334

Clipboard Image.png

这是菜鸟学python的第34篇原创文章


阅读本文大概需要8分钟

        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就讲到这里啦,希望能给初学者一些启发,若有什么不懂的,也可以留言跟我探讨交流.

最后说一下,原创真的不易,希望大家能够给点支持,欢迎转发转发转发,留言留言留言,重要的事情说三遍,也是对我的一点鼓励和动力.

也欢迎更多喜欢Python的同学关注 菜鸟学python,一起来学python吧 长按下方的二维码即可关注.

推荐 0
本文由 菜鸟学Python 创作,采用 知识共享署名-相同方式共享 3.0 中国大陆许可协议 进行许可。
转载、引用前需联系作者,并署名作者且注明文章出处。
本站文章版权归原作者及原出处所有 。内容为作者个人观点, 并不代表本站赞同其观点和对其真实性负责。本站是一个个人学习交流的平台,并不用于任何商业目的,如果有任何问题,请及时联系我们,我们将根据著作权人的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。

0 个评论

要回复文章请先登录注册