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

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

这是菜鸟学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__

>>

果然__enter()__和__exit__()都是file内置方法

step3:

3).好我们把代码改一下,调用__enter__(),read()和__exit()__函数看看

f=open(‘123.txt’)

var=f.__enter__()

print var

print f.read()

f.__exit__()

>>

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()#再读一遍文件

>>

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’

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()

阅读原文:

http://mp.weixin.qq.com/s?__biz=MzIxNjM4NDE2MA==&mid=2247483910&idx=1&sn=5a040c3da18ae86930a725a3bbbb1dec&chksm=978895c9a0ff1cdffb8a23c12158fb0dc4c26228ecea825f31873f82101838154d99d1c3d4d6#rd

    原文作者:菜鸟学python
    原文地址: https://www.jianshu.com/p/bd27f1513142
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞