zoukankan      html  css  js  c++  java
  • Python with

    简介

    在编程中会经常碰到这种情况:有一个特殊的语句块,在执行这个语句块之前需要先执行一些准备动作;当语句块执行完成后,需要继续执行一些收尾动作。例如,文件读写后需要关闭,数据库读写完毕需要关闭连接,资源的加锁和解锁等情况。

    对于这种情况python提供了上下文管理器(Context Manager)的概念,可以通过上下文管理器来定义/控制代码块执行前的准备动作,以及执行后的收尾动作。

    一、为何使用上下文管理器

    1. 上下文管理器提供了 __enter__()方法和__exit__()方法,在with语句中,如果用as指定了一个目标,会将__enter__()方法的返回值赋予这个目标。
    2. 运行中如果发生了异常,那么将会把异常的类型,值和追踪传递给__exit__()方法。如果__exit__()方法返回值为true,那么这个异常将会被抑制,否则这个异常将会被重新抛出。
    3. 如果没有发生异常,也会调用__exit__()方法,但是传入的参数为None, None, None。通常也是在这里放入代码进行如文件流/会话的关闭等操作。

    1、不使用上下文管理器的情况
    通过try...finally语句执行异常处理和关闭句柄的动作。

     1 #coding:utf8
     2 
     3 logger = open("log.txt", "a+")
     4 try:
     5     logger.write("hello world!
    ")
     6     
     7 finally:
     8     logger.close()
     9     f = open("log.txt","a+")
    10     for i in f.readlines():
    11         print i
    12     f.close()
    13 
    14 print logger.closed
    View Code
    hello world!
    
    True
    运行结果

    2、使用上下文管理器
    默认文件Python的内置file类型是支持上下文管理协议的。
    使用上下文管理器with使得依据精简了很多。

     1 #coding:UTF8
     2 with open("log.txt", "w") as logger:
     3     logger.write('Hello world!
    ')
     4 
     5 
     6 print logger.closed
     7 f = open(r'log.txt')
     8 for line in f.readlines():
     9     print line
    10 
    11 f.close()
    View Code
    True
    Hello world!
    运行结果

    二、实现上下文管理器实现上下文管理器有两种方式实现。方法一:类实现__enter__和__exit__方法。方法二:contextlib模块装饰器和生成器实现。
    下面我们通过两种方法分别实现一个自定义的上下文管理器。
    1、方法一:通过类实现__enter__和__exit__方法

     1 #coding:utf8
     2 
     3 class File(object):
     4     def __init__(self, file_name, method):
     5         self.file_obj = open(file_name, method)
     6     def __enter__(self):
     7         return self.file_obj
     8     def __exit__(self, type, value, traceback):
     9         self.file_obj.close()
    10 
    11 with File('demo.txt', 'w') as opened_file:
    12     opened_file.write('Hola!')
    View Code

    实现__enter__和__exit__方法后,就能通过with语句进行上下文管理。

    a、底层都发生了什么?

      1、with语句先暂存了File类的__exit__方法,然后它调用File类的__enter__方法。

      2、__enter__方法打开文件并返回给with语句,打开的文件句柄被传递给opened_file参数。

      3、with语句调用之前暂存的__exit__方法,__exit__方法关闭了文件。

    b、异常处理

      关于异常处理,with语句会采取哪些步骤。

      1. 它把异常的type,value和traceback传递给__exit__方法

      2. 它让__exit__方法来处理异常

      3. 如果__exit__返回的是True,那么这个异常就被忽略。

      4. 如果__exit__返回的是True以外的任何东西,那么这个异常将被with语句抛出。

     1 #异常抛出,_exit__返回的是True以外的任何东西,那么这个异常将被with语句抛出
     2 class File(object):
     3     def __init__(self, file_name, method):
     4         self.file_obj = open(file_name, method)
     5     def __enter__(self):
     6         return self.file_obj
     7     def __exit__(self, type, value, traceback):
     8         self.file_obj.close()
     9         print "type:",type
    10         print "value:",value
    11         print "traceback:",traceback
    12 
    13 
    14 with File('demo.txt', 'w') as opened_file:
    15     opened_file.undefined_function('Hola!')
    16 
    17 #output================================================
    18 
    19 # type: <type 'exceptions.AttributeError'>
    20 # value: 'file' object has no attribute 'undefined_function'
    21 # traceback: <traceback object at 0x000000000262D9C8>
    22 
    23 #     opened_file.undefined_function('Hola!')
    24 # AttributeError: 'file' object has no attribute 'undefined_function'
    异常抛出
     1 #异常忽略,__exit__返回的是True,那么这个异常就被忽略。
     2 class File(object):
     3     def __init__(self, file_name, method):
     4         self.file_obj = open(file_name, method)
     5     def __enter__(self):
     6         return self.file_obj
     7     def __exit__(self, exception_type, exception_value, traceback):
     8         print("Exception has been handled")
     9         self.file_obj.close()
    10         return True
    11 
    12 
    13 with File('demo.txt', 'w') as opened_file:
    14     opened_file.undefined_function('Hola!')
    15     
    16 # output==================================
    17 # Exception has been handled
    异常忽略:

    2、方法二:contextlib模块装饰器和生成器实现

    这种方式实现更优雅,我个人更喜欢这种方式。

    yield之前的代码由__enter__方法执行,yield之后的代码由__exit__方法执行。本质上还是__enter__和__exit__方法。

     1 # coding:utf-8
     2 import contextlib
     3 
     4 
     5 @contextlib.contextmanager
     6 def myopen(filename, mode):
     7     f = open(filename, mode)
     8     try:
     9         yield f.readlines()
    10     except Exception as e:
    11         print e
    12 
    13     finally:
    14         f.close()
    15 
    16 if __name__ == '__main__':
    17     with myopen(r'c:ip2.txt', 'r') as f:
    18         for line in f:
    19             print line
    View Code

     3、with语句上多个下文关联

    直接通过一个with语句打开多个上下文,即可同时使用多个上下文变量,而不必需嵌套使用with语句。

     1 class File(object):
     2     def __init__(self, file_name, method):
     3         self.file_obj = open(file_name, method)
     4     def __enter__(self):
     5         return self.file_obj
     6     def __exit__(self, exception_type, exception_value, traceback):
     7         self.file_obj.close()
     8         return True
     9 
    10 
    11 with File('demo.txt', 'w') as f1,File('demo.txt','w') as f2:
    12     print f1,f2
    13     
    14 # Output============================# <open file 'demo.txt', mode 'w' at 0x000000000263D150> <open file 'demo.txt', mode 'w' at 0x000000000263D1E0>
    View Code
  • 相关阅读:
    春节不回家
    夜间突然发烧,无法入眠
    歌词写得真好
    近日看到网上许多BBS寻找SAP及ABAP程序的学习资料,本人深知学习的艰辛与不易,特贡献自己多年的学习资料,完全免费
    人生的真谛
    SMARTFORMS的调用方法(作者:曹玉平)
    自己给自己当医生
    将ocx添加到.NET
    AQtime + ocx/dll
    ActiveX:创建安装:
  • 原文地址:https://www.cnblogs.com/qianyuliang/p/6434799.html
Copyright © 2011-2022 走看看