• python — 上下文管理器(例子:python连接数据库)


    上下文管理器

    概念

    上下文管理器是装饰器的近亲,与装饰器类似的,它们都是包装其他代码的工具 ,一般用在自动释放,资源回收上面

    • 装饰器:包装用于定义的代码块
    • 上下文管理器:可以包装任意格式的代码块

    什么是上下文管理器

    上下文管理器是一个包装任意代码块的对象,上下文管理器保证:

    • 进入上下文管理器时,每次代码执行的一致性
    • 当退出上下文管理器时,相关的资源会被正确地回收(即使内部代码出错,退出步骤也会执行)

    上下文管理器的应用

    • 上下文管理器被用到最多的就是——作为确保资源正确清理的一种方式

    上下文管理器的语法

    with语句

    • python2.5中加入的关键字with
    • with语句仅仅能对支持上下文管理协议的对象使用。
    • with语句的表达式的作用是返回一个遵循特定协议的对象,具体来说,该对象必须定义一个**__enter__方法和__exit__**方法

    支持上下文管理器协议的对象

    • file
    • decimal.Context
    • thread.LockType
    • threading.Lock
    • threading.RLock
    • threading.Condition
    • threading.Semaphore
    • threading.BoundedSemaphore

    with语句执行的解析

    • 语法:
      with context_expr as cm:
      do_sth()
    • 当with语句执行时,便执行上下文表达式(context_expr)来获得一个上下文管理器对象,上下文
      管理器的职责是提供一个上下文对象,用于在with语句块中处理细节:
      • 一旦获得了上下文对象,就会调用它的__enter__()方法
      • 将完成with语句块执行前的所有准备工作
      • 如果with语句后面跟了as语句,方法返回的结果会被赋值给as关键字后面的变量
      • 除了self参数,__enter__方法不接受任何其他参数。
      • __enter__方法一般负责执行一些配置
    • 当with语句块结束时,无论是正常结束,还是由于异常,都会调用上下文对象的__exit__()方法
      • __exit__()方法有3个参数
      • 如果with语句正常结束,三个参数全部都是 None
      • 如果发生异常,三个参数的值分别等于调用sys.exc_info()函数返回的三个值:类型(异常类)、值(异常实例)和跟踪记录(traceback),相应的跟踪记录对象。
      • 因为上下文管理器主要作用于共享资源,__enter__()和__exit__()方法基本是完成的是分配和释放资源的低层次工作,比如:数据库连接、锁分配、信号量加/减、状态管理、文件打开/关
        闭、异常处理等。

    __exit__ 的三个参数

    • exc_type
    • exc_val
    • exc_tb
    class ContestManger():
        def __init__(self,fp):
            self.fp = fp
        def __enter__(self):
            print("enter called")
        #必须接受三个参数
        def __exit__(self,exc_type,exc_val,exc_tb):
            print("exit called")
            print("exc_type:",exc_type)
            print("exc_val:",exc_val)
            print("exc_tb",exc_tb)
            self.fp.close()
            #return True
            #手动返回True,中止异常,上下文管理器包裹的代码后面的代码还可以正常运行,比如这里with cm01代码段不会继续运行,print("end..")会执行
    
    fp = open("test.txt","a+")
    cm01 = ContestManger(fp)
    with cm01:
        1/0
        print("wirte cm01")
        fp.write("this is cm01 test")
    print("end...........")
    
    输出:
    enter called
    exit called
    exc_type: <class 'ZeroDivisionError'>
    exc_val: division by zero
    exc_tb <traceback object at 0x0000020419D74500>
    Traceback (most recent call last):
      File "E:\python\tianlongbabu\2022-08-12 上下文管理器\01.上下文管理器.py", line 17, in <module>
        1/0
    ZeroDivisionError: division by zero
    #没有1/0的话三个参数就都是None
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    上下文管理器异常处理

    上下文管理器必须定义__exit__方法,该方法可以选择性地处理包装代码块中出现的异常,或者处理其他需要关闭上下文状态的事情
    __exit__方法接收了异常信息,就有处理这个异常的义务,通常可以做以下几件:

    • 传播异常
    • 中止异常
    • 抛出不同异

    使用contexttlib实现上下文管理器

    contextlib模块介绍

    • contextlib模块实现上下文自动管理
    • 这个生成器可以用更简单的方法创建上下文管理器
    from contextlib import contextmanager
    
    @contextmanager
    def mycontext():
        print("enter context")
        yield
        print("exit context")
    
    with mycontext():
        print("my context.....")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    使用上下文管理器连接数据库
    import pymysql
    class ConMysql():
        def __init__(self):
            self.con = None
            self.cursor_con = None
        def __enter__(self):
            print("connceting mysql ....")
            self.con = pymysql.connect(
                host="192.168.174.130",
                port=3306,
                user="sc",
                password="123456",
                database="sc"
            )
            self.cursor_con = self.con.cursor()
            return self
        def __exit__(self, exc_type, exc_val, exc_tb):
            if self.con:
                print("close mysql conncet...")
                self.con.close()
    
    # cm = ConMysql()
    with ConMysql() as cm:
        sql = "select * from userinfo"
        cm.cursor_con.execute(sql)
        print(cm.cursor_con.fetchall())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
  • 相关阅读:
    DataFrame创建介绍_大数据培训
    48.Java Lambda表达式
    Sentinel 概述
    【架构师视角系列】Apollo配置中心之架构设计(一)
    Spring Boot + Vue的网上商城之商品管理
    基于SpringBoot的毕业论文管理系统的设计与实现(开题报告)
    【文件传输】查找等相关命令
    [TFF学习]官方教程jupyter运行记录_联邦学习之图像分类任务_1
    让python代码找到文件路径的最好方法
    Python 实战:用 Scrapyd 打造个人化的爬虫部署管理控制台
  • 原文地址:https://blog.csdn.net/a1991376352/article/details/126303708