关于python的with语句使用_第1页
关于python的with语句使用_第2页
关于python的with语句使用_第3页
关于python的with语句使用_第4页
关于python的with语句使用_第5页
已阅读5页,还剩17页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、巧用with语句让python程序更优秀概述 学习Python有一段时间了,最近做一个项目会涉及到文件的读取和关闭。比如:我想把一些对象序列化到文件里面,然后当我再次使用的时候,在从文件里面读取反序列化成对象。像这种操作一般都是用tryexceptfinally。但是经过自己对Python的研究发现会有更出色的方法,比如:with-as语句也有的人称为context manager。With-As我们先看一下例子,当我们需要打开一个文件的时,比如:txt等,一般经常会这么操作:1234567try:    f = file.open('test.

2、txt','rw')    To Doexcept:    To Dofinally:    f.close()这是错误,因为file.open是否打开文件是不确定,而在出现异常的时候你却关闭了已经打开的文件。文件没有打开怎么能直接关闭呢?你可以按照下面的解决方法来解决上述出现的问题。123456789101112131415try:    f = file.open('test.txt',

3、9;rw')    To Doexcept:    To Do    /出现异常直接返回或者退出,这说明file并没有打开。    return/exit(-1)  /已经成功打开file文件,所以你需要在finally中关闭打开的文件。try:    To Doexcept:    To Dofinally:  

4、60; f.close()你会发现这么做会非常麻烦,并且tryexcept.finally嵌套也比较啰嗦。那有没有好的解决办法能解决上述问题,并且还能减少代码量呢?(类似于C#中的using关键字)答案是肯定的,那就是with.as语句。With语句适用于对I/O、文件流、数据流等资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的“清理”操作,释放资源,比如文件使用后自动关闭、线程中锁的自动获取和释放等等。with语句的用法12with expression as variable:    with-block表达式被计算(求值)

5、依赖一个支持Context Management Protocol(必须包含:_enter_()和_exit_()的方法) 的对象。因为expression返回一个Context Manager(上下文管理器)对象,如果指定了as variable会将上下文管理器对象_enter_() 方法的返回值赋值给variable;如果没有指定as variable则丢弃 。with -block在执行语句体之前会调用上下文管理器的_enter_() 方法,执行完语句体之后会执行 _exit_() 方法,即使代码出现异常也会运行“清理”代码。例如如下代码

6、:1234with open('/etc/passwd', 'r') as f:    for line in f:        print line        . more processing code .这个语句执行完成之后,不管在处理文件过程中是否发生异常,都能保证 with 语句执行完毕后已经关闭了打开的文件句柄,确实比tryexceptfinall

7、y好多了。在这个例子中f就是上下文管理器_enter_()的返回值,返回的是当前文件自身的引用。Python内建对象都加入了对上下文管理器的支持,可以用在with语句中。比如:file、 threading、decimal等等,在多线程模块中,lock和条件变量也是支持with语句的。例如:1234lock = threading.Lock()with lock:    # Critical section of code    .在代码执行之前lock总是先获得,只要block代码完成lock就会被释放。要想彻底

8、了解Python的With-As语句,请继续往下看。Python 术语Context Management Protocol(上下文管理协议):包含方法 _enter_() 和 _exit_(),支持该协议的对象要实现这两个方法。上下文管理器(Context Manager):支持上下文管理协议的对象,这种对象实现了_enter_() 和 _exit_() 方法。上下文管理器定义执行 with 语句时要建立的运行时上下文,负责执行 with 语句块上下文中的进入与退出操作。通常使用 with 语句调用上下文管理器,也可以通过直接调用其方法来使用。12345678910111

9、2131415161718192021    context_manager = context_expression    exit = type(context_manager)._exit_      value = type(context_manager)._enter_(context_manager)    exc = True   # True 表示正常执行,即便有异常也忽略;Fa

10、lse 表示重新抛出异常,需要对异常进行处理    try:        try:            target = value  # 如果使用了 as 子句            with-body 

11、    # 执行 with-body        except:            # 执行过程中有异常发生            exc = False       

12、60;    # 如果 _exit_ 返回 True,则异常被忽略;如果返回 False,则重新抛出异常            # 由外层代码对异常进行处理            if not exit(context_manager, *sys.exc_info():    &

13、#160;           raise    finally:        # 正常退出,或者通过 statement-body 中的 break/continue/return 语句退出        # 或者忽略异常退出     

14、   if exc:            exit(context_manager, None, None, None)         # 缺省返回 None,None 在布尔上下文中看做是 False上下文管理协议的高层解释:· with表达式执行生成一个叫做上下文管理器的对象,上下文管理器必须包含_enter_()和_exit_()方法,并且要实现该

15、两个方法。· 上下文管理器的_enter_()方法被调用,返回值将赋值给var,如果没有as var,则返回值被丢弃。· 执行With-Body语句体。· 不管是否执行过程中是否发生了异常,执行上下文管理器的 _exit_() 方法,_exit_()方法负责执行“clean-up”工作,如释放资源等。如果执行过程中没有出现异常,或者语句体中执行了语句( break/continue/return),则以 None 作为参数调用 _exit_(None, None, None) ;如果执行过程中出现异常,则使用 sy

16、s.exc_info 得到的异常信息为参数调用_exit_(exc_type, exc_value, exc_traceback),通常返回值是一个tuple, (type, value/message, traceback)。· 出现异常时,如果 _exit_(type, value, traceback) 返回 False,则会重新抛出异常,让with 之外的语句逻辑来处理异常,这也是通用做法;如果返回 True,则忽略异常,不再对异常进行处理。运行时上下文(runtime context):通过上下文管理器创建,并由上下文管理器的_enter_()

17、 和_exit_() 方法实现,_enter_() 方法在语句体执行之前进入运行时上下文,_exit_() 在语句体执行完后从运行时上下文退出。返回一个布尔值表示是否对发生的异常进行处理。如果退出时没有发生异常,则3个参数都为(None,None,None)。如果发生异常,返回True :不处理异常,否则会在退出该方法后重新抛出异常以由 with 语句之外的代码进行处理。如果该方法内部产生异常,不能重新抛出通过参数传递进来的异常,只需要return False 就可以。之后,上下文管理代码会检测是否 _exit_() 失败来捕获和处理异常。1234567

18、8910111213141516171819202122232425262728293031323334#!/usr/bin/env python#-*- coding: utf-8 -*- class Cursor(object):    def execute(self,msg):        print msg class DatabaseConnection(object):    def commit(s

19、elf):        print "Commits current transaction"    def rollback(self):        print "Rolls back current transaction"    def _enter_(self):   

20、60;    print "Go into _enter_()"        cursor = Cursor()        return cursor    def _exit_(self,exc_type,exc_value,exc_tb):       

21、60;print "Go into _exit_()"        #raise Exception("_exit_.Exception")        if exc_tb is None:            #如果没有异常,则提交事务   

22、         print "Exited Without Exception"            mit()        else:            #如

23、果有异常,则回滚            print "Exited with exception raised"            print "type:",exc_type,",value:",exc_value,",exc_tb:",exc_tb,""

24、;            self.rollback()            if _name_="_main_":    db_connection = DatabaseConnection()    with db_connection as curso

25、r:        cursor.execute("insert into.")        cursor.execute("delete from.")代码运行效果如下:123456Go into _enter_()insert into.delete from.Go into _exit_()Exited Without ExceptionCommits current transactio

26、n上述代码正好验证了我们之前的分析,当运行with db_connection运行时,进入我们自定义的_enter_()方法,当执行完with包裹的代码块时,就会进入_exit_()方法,如果没有异常(通过exc_tb是否为None来判断,当然也可以用其他两个参数判断。)则执行相应的代码逻辑。1234567891011121314151617181920212223242526272829303132333435#!/usr/bin/env python#-*- coding: utf-8 -*- class Cursor(object):   

27、; def execute(self,msg):        print msg class DatabaseConnection(object):    def commit(self):        print "Commits current transaction"    def rollback(self

28、):        print "Rolls back current transaction"    def _enter_(self):        print "Go into _enter_()"        cursor = Cursor()  

29、      return cursor    def _exit_(self,exc_type,exc_value,exc_tb):        print "Go into _exit_()"        #raise Exception("_exit_.Exception") &#

30、160;      if exc_tb is None:            #如果没有异常,则提交事务            print "Exited Without Exception"       &

31、#160;    mit()        else:            #如果有异常,则回滚            print "Exited With Exception raised"  &

32、#160;         print "type:",exc_type,",value:",exc_value,",exc_tb:",exc_tb,""            self.rollback()        

33、60;   if _name_="_main_":    db_connection = DatabaseConnection()    with db_connection as cursor:        cursor.execute("insert into.")        r

34、aise Exception("raise exception")        cursor.execute("delete from.")该代码示例中,我在with包裹的代码块中造成一个异常。我们来看一下效果:12345678910Go into _enter_()insert into.Go into _exit_()Exited With Exception raisedtype: <type 'exceptions.Exception'>

35、 ,value: raise exception ,exc_tb: <traceback object at 0x0252A878> Rolls back current transactionTraceback (most recent call last):  File "D:TestDatabaseConnection.py", line 34, in <module>    raise Exception("raise exception")Exception:

36、raise exception当with包裹的代码块一旦出现异常,则进入_exit_()方法内,并根据该方法的参数全不为None。如果你在_exit_方法内你不手动返回一个值的话,则默认返回False。如果你返回True,则不会捕捉该异常,即使你在with代码块最外面包裹一个tryexceptfinally也不会捕捉到该异常,如果返回False则with之外的tryexcept也能捕捉到。1234567891011121314151617181920212223242526272829303132333435363738394041#!/usr/bin/env python#-*- codin

37、g: utf-8 -*- import sys class Cursor(object):    def execute(self,msg):        print msg class DatabaseConnection(object):    def commit(self):        print "Co

38、mmits current transaction"    def rollback(self):        print "Rolls back current transaction"    def _enter_(self):        print "Go into _enter_()" 

39、;       cursor = Cursor()        return cursor    def _exit_(self,exc_type,exc_value,exc_tb):        print "Go into _exit_()"     

40、   #raise Exception("_exit_.Exception")        if exc_tb is None:            #如果没有异常,则提交事务            print "E

41、xited Without Exception"            mit()        else:            #如果有异常,则回滚          

42、;  print "Exited With Exception raised"            print "type:",exc_type,",value:",exc_value,",exc_tb:",exc_tb,""           

43、 self.rollback()        return True            if _name_="_main_":    db_connection = DatabaseConnection()    try:     &

44、#160;  with db_connection as cursor:            cursor.execute("insert into.")            raise Exception("raise exception")    &#

45、160;       cursor.execute("delete from.")    except:        print"包裹with语句的try ",sys.exc_info()运行效果如下:123456Go into _enter_()insert into.Go into _exit_()Exited With Exception raisedtype

46、: <type 'exceptions.Exception'> ,value: raise exception ,exc_tb: <traceback object at 0x0252A878> Rolls back current transaction基本上Python的常用的用法我就了解这么多,至于代码希望你动手试一下你也能了解Python with语句的原理。The contextlib modulecontextlib模块支持一些函数和装饰器,比如:装饰器 contextmanager、函数 nested 和上下文管理器closing。使用这些

47、对象,可以对已有的生成器(yield)函数或者对象进行包装,加入对上下文管理协议的支持,这样可以避免专门编写上下文管理器来支持 with 语句。装饰器 contextmanagercontextmanager 用于对生成器(yield)函数进行装饰,生成器(yield)函数被装饰以后,返回的是一个ContextManager(上下文管理器),其 _enter_() 和 _exit_() 方法由 contextmanager 负责提供,而不是之前通过一个上下文管理器重写这两个方法了。被装饰的函数只能产生一个值,否则会导致异常 RuntimeError;并且会把yie

48、ld的值赋值给as后面的变量。看一下例子:12345678910111213141516171819202122232425262728293031323334353637383940414243#!/usr/bin/env python#-*- coding: utf-8 -*- import sysfrom contextlib import contextmanager class Cursor(object):    def execute(self,msg):     

49、60;  print msg class DatabaseConnection(object):    def cursor(self):        print "Create a instance of Cursor"        return Cursor()    def commit(self)

50、:        print "Commits current transaction"    def rollback(self):        print "Rolls back current transaction"     contextmanagerdef db_transaction(conn

51、ection):    cursor = connection.cursor()    try:        print "yeild 执行之前."        yield cursor        print "yeild 执行之后.&quo

52、t;    except:        print "db_transaction raise exception"        connection.rollback()    else:        print"Existed withou

53、t exception"        mit() if _name_="_main_":    db_connection = DatabaseConnection()    try:        with db_transaction(db_connection) as cursor:  &#

54、160;         cursor.execute("insert into.")            #raise Exception("raise exception")            cursor.execut

55、e("delete from.")    except:        print sys.exc_info()运行结果如下:1234567Create a instance of Cursoryeild 执行之前.insert into.delete from.yeild 执行之后.Existed without exceptionCommits current transaction通过上述运行结果,可以看出,生成器函数中 yield 之前的语句在 _

56、enter_() 方法中执行,yield 之后的语句在 _exit_() 中执行,而 yield 产生的值赋给了 as 后面的 变量。这个contextmanager修饰器 只是省略了 _enter_() / _exit_() 的编写,但并不负责实现“获取资源”和“清理资源”工作;“获取资源”操作需要定义在 yield 语句之前,“清理资源”操作需要定义 yield 语句之后,这样 with 语句在执行 _enter_() / _exit_() 方法时会执行这些语句以获取/释放资源,即生成器函数中需要实现必要的逻辑控制,包括资源访问出现错误时抛出适当的异常。nested(m

57、gr1, mgr2, .)()contextlib模块也有个nested(mgr1, mgr2, .)()函数,这个函数可以作用在多个上下文管理器。例如下面的例子with 语句不仅开启一个transaction也获得了一个线程锁,让当前操作不被其他线程干扰。1234567891011121314151617181920212223242526272829303132333435363738394041#!/usr/bin/env python#-*- coding: utf-8 -*- import sysfrom contextli

58、b import contextmanager,nested,closingimport threading class Cursor(object):    def execute(self,msg):        print msg class DatabaseConnection(object):    def cursor(self):      

59、  print "Create a instance of Cursor"        return Cursor()    def commit(self):        print "Commits current transaction"    def rollback(self):        print "Rolls back current transaction"    

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论