zoukankan      html  css  js  c++  java
  • 上下文管理器的使用

    通常在涉及确保某种资源以一种期望的方式被初始化和反初始化,或者尽力去避免重复时使用上下文管理器。

    编写管理器常见的几个环境场所:

    1、资源清理

    打开和关闭资源(如文件或者数据库连接)是编写上下文管理器的重要因素之一。确保出现异常时正确关闭资源往往很重要,这样能够避免最终随着时间的推移而产生很多的僵尸进程。

    上下文管理器的优势就在于此,通过在__enter__方法中打开资源并返回它,可以保证__enter__方法能执行,同时也能在异常出现之前关闭这个资源。

    -看如下打开mysql数据库连接的上下文管理器:

    import json
    
    import pymysql
    
    #todo 单点连接数据库
    class DB_LINK():
        def __init__(self):
            self.pool_db = pymysql.connect(host='127.0.0.1',port=3306,user='root',passwd='123456',db='test',charset='utf8',cursorclass=pymysql.cursors.DictCursor)
            
        def __enter__(self):
            self.coon = self.pool_db.cursor()
            return  self.coon
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            self.coon.close()
    
    #在上下文管理器中,可以针对数据库执行查询操作并检索结果。
    with DB_LINK() as db:
        db.execute('select * from user where age<2 and username="qxh" and id<500')
        print(db.fetchall())
    [{'id': 52, 'username': 'qxh', 'age': 1}, {'id': 128, 'username': 'qxh', 'age': 1}, {'id': 134, 'username': 'qxh', 'age': 1}, {'id': 229, 'username': 'qxh', 'age': 1}, {'id': 250, 'username': 'qxh', 'age': 1}, {'id': 426, 'username': 'qxh', 'age': 1}]

    上下文管理器创建了一个mysql连接对象并且返回另一个指针,开发人员可以使用该指针与数据库交互。当上下文管理器存在时,保证连接是处于关闭状态仍很重要。因为占用数据库的连接不仅消耗内存,而且在应用主机和数据库主机上它们也会打开文件或者端口。此外有些数据库也有最大连接数的阈值。

    这个上下文管理器不仅仅在__enter__方法的结尾返回自身,还返回一个数据库的指针。这个示例很实用,但它,执行的仍然是上下午管理器的__exit__方法。

    大多数与数据库相关的框架都会处理数据库连接的打开或关闭操作,但原则依然是:

    如果打开一个资源,一定要确保正确的关闭它,此时上下文管理器是一个十分优秀的工具。

    2、避免重复

    当提到避免重复时,异常处理是最为常见的。上下文管理器能够传播和终止异常,这使得最合适将它与except子句放到同一个地方定义。

    2.1、传播异常

    __exit__方法只是向流程链上传播异常,这是通过返回False实现的,根本不需要与异常实例交互。考虑下面的上下文管理器:

    class BubbleExceptions():
        def __enter__(self):
            return self
        def __exit__(self, exc_type, exc_val, exc_tb):
            if exc_val:
                print('Bubbling up exception :%s.'%exc_val)
            return False
    
    with BubbleExceptions() as f:
        print(5/5)
    print(5/0)
    #在上下文管理器中运行普通代码块(不抛出异常)将不会做什么特别的事情。
    1.0

    #该代码抛出异常时: Bubbling up exception :division by zero. Traceback (most recent call last): File "C:/Users/Administrator/Desktop/test/mysql_pool_test.py", line 125, in <module> print(5/0) ZeroDivisionError: division by zero

     在此有两件值得注意的重要事情,第一个输出行(Bubbling up exception ...开头)由__exit__方法自身产生,它对应于__exit__方法第二行中的print语句。这意味着__exit__方法的确运行了且已完成,因为该方法返回了False,所以被首先发送给__exit__的异常只是被重新抛出了。

    2.2、终止异常

    如前所述,__exit__方法拥有的另一个选项就是终止它所接收的异常。下面的上下文管理器终止所有可能发送给__exit__方法的异常(切记使用)

    class BubbleExceptions():
        def __enter__(self):
            return self
        def __exit__(self, exc_type, exc_val, exc_tb):
            if exc_val:
                print('Bubbling up exception :%s.'%exc_val)
            return True
    
    with BubbleExceptions() as f:
        print(5/5)
        print(5/0)
    1.0
    Bubbling up exception :division by zero.

     代码的主要区别是__exit__方法返回的是True而不是False.

    注意的三点:

    1,回溯消失了,由于异常被__exit__方法处理了(终止),因此程序没有引发异常,继续执行。

    2,没有返回任何值。

    3,在print(5/0)后面的所有代码都不会再执行。

    2.3、处理特定异常类

    一个简单的异常处理函数__exit__可以仅检查异常是否是特定异常类的实例,执行任何必要的异常处理,并根据是否获得其他类型的异常返回True或False。

    class BubbleExceptions():
        def __enter__(self):
            return self
        def __exit__(self, exc_type, exc_val, exc_tb):
            print('exc_type:',exc_type)
            if not exc_type:
                return True
            if issubclass(exc_type,ZeroDivisionError):
                print('Bubbling up exception :%s.'%exc_val)
                return True
            return False
    
    with BubbleExceptions() as f:
        print(5/5)
        print(5/0)
        print('xxxxxxxxxxxxx')
    执行结果:
    1.0 exc_type: <class 'ZeroDivisionError'> Bubbling up exception :division by zero.

    2.4、不包括的子类

    如何完成类或实例的检查也可以更加灵活。例如,假如想要捕获一个给定的异常类,但是不希望显示的捕获它的子类。也可以理解针对性的处理异常类

    把上文中的if issubclass(exc_type,ZeroDivisionError): 变更为if exc_type==ZeroDivisionError。

    2.5、基于属性的异常处理(不做过多的解释了)

    下一章介绍一个更加简单的语法:

    python标准库@contextlib.contextmanager

    Python标准库还提供了更加易用的上下文管理器工具模块contextlib,它是通过生成器实现的,我们不需要再创建类以及__enter__和__exit__这两个特俗的方法。

  • 相关阅读:
    重定向管道
    系统安全
    Linux启动流程
    压缩解压
    Vim
    ssh
    sudo
    Raid
    rsync
    quota
  • 原文地址:https://www.cnblogs.com/qxh-beijing2016/p/15561472.html
Copyright © 2011-2022 走看看