Python:with语句和上下文管理器对象

今天看书,书上面提到要尽量使用with自动关闭资源,里面还提到了上下文管理器对象的概念,然后查找资料,对with的解释如下:with的有一些任务,可能事先需要设置,事后做清理工作。对于这种场景,Python的with语句提供了一种非常方便的处理方式。一个很好的例子是文件处理,需要获取一个文件句柄,从文件中读取数据,然后关闭文件句柄。下面就讲下with语句以及上下文管理器对象,希望对你有帮助。

(一)with语句

    对于文件操作完成后,应该要关闭它,这是一个常识,因为打开的文件不仅占用了系统资源,而且可能影响其它程序或进程的操作,甚至会导致用户期望与实际操作结果不一样。with语句得语法为

    with  表达式  [as 目标]:

            代码块

with语句支持嵌套,支持多个with子句,它们两者可以相互转换。"with expr1 as e1,expr2 as e2"与下面的嵌套形式等价

1
2
with expr1 as e1:
     with expr2 as e2:

with语句使用比较简单。如下面例子不使用with的时候代码如下

1
2
3
=  open ( 'test.txt' , 'w' )
f.write( "hello" )
f.close() #这句很容易被忘记,这也是为什么推荐使用with

使用with语句代码如下:

1
2
with  open ( 'test.txt' , 'w' ) as f:
     f.write( "hello" )

    with语句可以在代码块执行完毕后,还原到进入该代码块时的现场(这句话要仔细理解,也就是说with里的代码块执行完后,会返回到刚刚进入with时的现场)。with语句代码块执行过程如下:

(1)计算表达式的值,返回一个上下文管理器对象

   (2)加载上下文管理器对象的__exit__()方法以备后用

(3)调用上下文管理器对象的__enter__()方法

(4)如果with语句中设置了目标对象,则将__enter__()方法的返回值赋值给目标对象(比如上面的f)

(5)执行with里的代码块

(6)如果步骤(5)代码正常结束,调用上下文管理器对象的__exit__()方法,返回值直接忽略

(7)如果步骤(5)中代码异常,调用上下文管理器对象的__exit__(),并将异常类型、值以及traceback信息作为参数传递给__exit__()方法。如果__exit__()返回值为false,则异常会被重新抛出;如果返回的是true,异常被挂起,程序继续执行

   使用with的好处是无论程序以何种方式跳出with块,总能保证资源被正确关闭。下面介绍一下上下文管理器对象。


(二)上下文管理器对象

    with的神奇之处得益于一个成为上下文管理器的(context manager)的东西,它用来创建一个这样的对象:它定义程序运行时需要建立的上下文,处理程序的进入和退出,实现上下文管理协议,即在对象中定义__enter__()和__exit__()方法(这两个方法可以重载,这就说明,我们可以自定义属于自己的上下文管理器,待会儿再介绍),其中:

   __enter__(self):进入运行时的上下文,也就是进入上下文管理器时调用该函数,返回运行时的上下文对象,with语句中会将这个返回值绑定到目标对象上(上面的例子就是绑定到f上)。顺便说下上下文表达式(Context Expression),上下文表达式指with 语句中跟在关键字 with 之后的表达式,该表达式要返回一个上下文管理器对象,该对象就被赋值给了目标对象。

    __exit__(self,exception_type,exception_value,traceback):退出运行时的上下文,定义在块执行(或终止)之后上下文管理器应该做什么。它可以处理异常、清理现场或者处理with块中语句执行完成后需要处理的动作。exception_type,exception_value,traceback三个参数代表的意思分别是异常的类型、值和追踪信息。如果没有异常,3个参数均设为None。此方法返回值为True或者False,分别指示被引发的异常得到了还是没有得到处理。如果返回False,引发的异常会被传递出上下文。这个在前面简单的提到过,希望你能结合上下文仔细理解这些东西。


    实际上任何实现了上下文协议的对象都可以称为一个上下文管理器,文件也是实现了这个协议的上下文管理器,它们都能够与with语句兼容。文件对象的__enter__()和__exit__()属性如下

1
2
3
4
>>>f.__enter__
- in  method __enter__ of  file  object  at  0x029F0700 >
>>>f.__exit__
- in  method __exit__ of  file  object  at  0x029F0700 >


        当然我们也可以定义自己的上下文管理器,只要实现了上下文协议便可以和with语句一起使用。如下面例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class  OpenFile( object ):
     def  __init__( self ,filename,mode):
         self .filename = filename
         self .mode = mode
     def  __enter__( self ):
         self .f = open ( self .filename, self .mode)
         return  self .f   #作为as说明符指定的变量的值
     def  __exit__( self ,exception_type,exception_value,traceback):
         if  exception_type  is  None : #如果没有异常,正常关闭资源
             self .f.close()
        
         else : #有异常发生
             print  exception_value
             print  traceback
             return  False #返回false则异常会被重新抛出
         
with OpenFile( 'my_file.txt' , 'w' ) as f:
     f.write( 'Hello' )
     f.write( 'World' )

    上下文管理器主要作用于资源共享,因此在实际应用中__enter__()和__exit__()方法基本用于资源分配以及释放相关的工作,如打开/关闭文件、异常处理、断开流的连接、锁分配等。为了更好的辅助上下文管理器,Python还提供了contextlib模块,这个下次有机会再讲。技术有限,不正之处,请多多包涵!

你可能感兴趣的:(Python语法相关)