zoukankan      html  css  js  c++  java
  • python上下文管理器

    资源管理

    在任何编程语言中,对于资源的使用诸如文件操作或数据库连接等非常普遍。 但是这些资源是有限的,因此我们要确保使用这些资源后及时释放他们, 否则将导致资源泄漏,甚至会使系统变慢或崩溃。 在Python中,可以通过使用上下文管理器来实现对于资源的分配和及时释放,这有助于对资源的正确处理。 执行文件操作的最常见方式是使用with关键字,假设你有两个相关的操作要成对执行,并且这两个相关操作之间还有一段代码,我们就可以使用上下文管理器实现这个目的。 基本语法格式为:

    with EXPR as VAR:
        BLOCK

    以文件操作为例,当我们打开一个文件,就会消耗一个文件描述符.文件描述符属于有限的资源,因此一个进程一次只能打开有一定数量的文件,如果们打开了太多文件而且没有及时关闭就会造成文件描述符泄露.下面用代码来解释这个问题

    >>> file_descriptors = []
    >>> for x in range(100000):
    ...     file_descriptors.append(open('test.txt', 'w'))
    ... 
    Traceback (most recent call last):
      File "<stdin>", line 2, in <module>
    OSError: [Errno 24] Too many open files: 'test.txt'

    使用上写文管理器进行资源管理

    如果代码块抛出异常,或者代码块包含具有多个返回路径的复杂算法,则在每个位置逐一关闭文件将变得很麻烦.在使用其他语言时,使用try-except-finally可以确保即使有异常也可以在使用后关闭文件资源.Python提供了一种更简单的方法来管理资源:上下文管理器. 使用with关键字例如:

    with open("test.txt", 'w') as f: 
        f.write('Hello World!')

    这段代码执行打开文件,写进数据,关闭文件操作.如果在写进数据的时候抛出错误,上下文管理器会尝试解决这个错误, 它与下边这段代码的作用是一样的.

    file = open('test.txt', 'w')
    try:
        file.write('Hello World!')
    finally:
        file.close()

    比较这两段代码我们就会发现使用with使代码更加精简,但使用with的最主要好处是他能够确保关闭文件,且不需要考虑嵌套块退出的方式.

    上下文管理器的常见用例是占用和释放资源以及关闭打开的文件.接下来我们尝试自己实现一个上下文管理器,从而对他有一个更好的理解

    实现上下文管理器

     上下文管理器可以使用类或函数(带有装饰器)来编写,当我们用类实现上下文管理器时,要确保类中含有 __enter__() 和 __exit__()方法.  __enter__() 用来返回要被使用的资源,__exit__() 执行清除操作.接下来我们用两个例子解释如何用类创建上下文管理器.

    >>> class ContextManager(): 
    ...     def __init__(self): 
    ...         print('init method called') 
    ...           
    ...     def __enter__(self): 
    ...         print('enter method called') 
    ...         return self
    ...       
    ...     def __exit__(self, exc_type, exc_value, exc_traceback): 
    ...         print('exit method called') 
    ... 
    >>> with ContextManager() as manager: 
    ...     print('with statement block')
    ... 
    init method called
    enter method called
    with statement block
    exit method called

    从输出结果中我们不难看出这段代码执行顺序如下

    __init__()
    __enter__()
    code inside the with block
    __exit__()

    接下来以文件操作为例来解释如何用类来创建上下文管理器

    >>> class FileManager(): 
    ...     def __init__(self, filename, mode): 
    ...         self.filename = filename 
    ...         self.mode = mode 
    ...         self.file = None
    ...           
    ...     def __enter__(self): 
    ...         self.file = open(self.filename, self.mode) 
    ...         return self.file
    ...       
    ...     def __exit__(self, exc_type, exc_value, exc_traceback): 
    ...         self.file.close() 
    ... 
    >>> with FileManager('demo.txt', 'w') as f:
    ...     f.write('Hello World!')
    ... 
    >>> print(f.closed) 
    True
    >>> 

    执行上述代码,实际运行过程如下

    1. with语句把__exit__方法 存储在 FileManager中.
    2. 调用 FileManager类中的__enter__方法
    3. __enter__方法以写模式打开文件并返回这个FileManager对象
    4. 将__enter__方法的返回值赋值给变量f
    5. 利用.write()在文件中写入"Hello World"
    6. with调用__exit__方法
    7. __exit__关闭文件

    异常处理

    __exit__方法有三个参数:exc_type, exc_val, exc_tb。如果with下代码块发生异常并退出,那么分别对应异常的type、value 和 traceback。否则三个参数全为None。具体来说,如果在第四到第六步有异常抛出,Python会把异常的type、value 和 traceback传给__exit__方法.之后由__exit__方法决定如何关闭文件以及其他后续操作.实际运行过程如下

    1. with把异常的type、value 和 traceback传给__exit__方法
    2. __exit__处理异常
    3. 如果__exit__返回True则说明异常被成功处理
    4. 如果__exit__返回其他值,则异常会被with抛出

    在我们自定义的代码中__exit__方法返回的是None,因此如果我们尝试访问一个不存在的函数,with语句则会抛出如下异常

    >>> with FileManager('demo.txt', 'w') as f:
    ...     f.undefined_function('haha')
    ... 
    Traceback (most recent call last):
      File "<stdin>", line 2, in <module>
    AttributeError: '_io.TextIOWrapper' object has no attribute 'undefined_function'
    >>> 

    修改__exit__方法来处理异常

    >>> class FileManager(): 
    ...     def __init__(self, filename, mode): 
    ...         self.filename = filename 
    ...         self.mode = mode 
    ...         self.file = None
    ...           
    ...     def __enter__(self): 
    ...         self.file = open(self.filename, self.mode) 
    ...         return self.file
    ...        
    ...     def __exit__(self, exc_type, exc_value, exc_traceback): 
    ...         print("Exception has been handled")
    ...         self.file.close()
    ...         return True
    ... 
    >>> with FileManager('demo.txt', 'w') as f:
    ...     f.undefined_function()
    ... 
    Exception has been handled
    >>> 

    修改后的方法返回True, 表示这个异常已经被(以某种方法)处理了,with也不会再抛出异常

  • 相关阅读:
    [git] git 的基本认知
    [Java] Java IO Files
    [Java] Java IO 概况
    [Java] JavaMail 发送带图片的 html 格式的邮件
    [Java] HashMap 导致的高 CPU 使用率
    [Struts] Hello World Demo
    [Hibernate] 注解映射例子
    [Hibernate] List 映射例子
    cmd的xcopy命令
    wpf custom control
  • 原文地址:https://www.cnblogs.com/laozhanghahaha/p/12318460.html
Copyright © 2011-2022 走看看