知行编程网知行编程网  2022-02-27 04:00 知行编程网 隐藏边栏 |   抢沙发  1 
文章评分 0 次,平均分 0.0

 

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

本文为原创文章,版权归所有,欢迎分享本文,转载请保留出处!

知行编程网
知行编程网 关注:1    粉丝:1
这个人很懒,什么都没写

发表评论

表情 格式 链接 私密 签到
扫一扫二维码分享