Python-上下文管理

发布于 2020-06-01  746 次阅读


在python只要实现了__enter__、__exit__方法,即支持上下文管理器协议。上下文管理器就是支持上下文管理器协议的对象,它是为了with而生。当with语句在开始运行时,会在上下文管理器对象上调用__enter__方法,运行结束后会调用__exit__方法( 必须在这个对象的类中同时声明__enter__和__exit__方法 )。

class Open:
    def __init__(self, name):
        self.name = name

    def __enter__(self):
        print('出现with语句,__enter__方法被触发,有返回值则赋值给as后声明的变量')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with执行结束执行我------')
        print(exc_type) # 异常类型
        print(exc_val)  # 异常值
        print(exc_tb)   # 异常追溯信息


with Open('a.text') as f:
    print('执行-------')
    raise AttributeError('不知道为什么要报错')

如果__exit__方法返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行

模拟open

class Open:
    def __init__(self, filepath, mode='r', encoding='utf-8'):
        self.filepath = filepath
        self.mode = mode
        self.encoding = encoding

    def __enter__(self):
        print('出现with语句,__enter__方法被触发,有返回值则赋值给as后声明的变量')
        self.f = open(self.filepath, self.mode, encoding=self.encoding)
        return self.f

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with执行结束执行我------')
        print(exc_type) # 异常类型
        print(exc_val)  # 异常值
        print(exc_tb)   # 异常追溯信息

        return True # 如果没有返回true with发生异常。后面代码则不会执行


with Open('a.text', 'w') as f:
    print('执行-------')
    f.write('hello world')

#################################################
出现with语句,enter方法被触发,有返回值则赋值给as后声明的变量
 执行-------
 with执行结束执行我------
 None
 None
 None

模拟mysql连接

import pymysql

class DB:
    def __init__(self, username, password, host='127.0.0.1', port=3306, db='', charset='utf8'):
        self.name = username
        self.passwd = password
        self.host = host
        self.port = port
        self.db = db
        self.charset = charset

    def __enter__(self):
        # 创建数据库连接
        self.conn = pymysql.connect(user=self.name, passwd=self.passwd, host=self.host, port=self.port, db=self.db, charset=self.charset)

        # 创建游标
        self.dbcur = self.conn.cursor()

        # 返回游标
        return self.dbcur

    def __exit__(self, exc_type, exc_val, exc_tb):
        # 提交事务
        self.conn.commit()

        # 关闭游标
        self.dbcur.close()

        # 关闭连接
        self.conn.close()

with DB('root', '123456', db='mysql') as db:
    db.execute('select host,user from user')
    print(db)
    for i in db:
        print(i)

######################################################
<pymysql.cursors.Cursor object at 0x0000000002DAC160>
('127.0.0.1', 'root')
('::1', 'root')
('localhost', '')
('localhost', 'root')

总结

  • with DB -----》触发DB.__enter__
  • as db -----》DB.__enter__ 的返回值
  • 没有异常的情况下,整个代码块运行完毕后去触发__exit__,它的三个参数都为None
  • 有异常的情况下,从异常出现的位置直接触发__exit__
    • 如果__exit__的返回值为True,代表忽略了异常
    • 如果__exit__的返回值为False,代表主动抛出异常
    • __exit__运行完毕,代表with语句已全部执行结束

with的好处

  • 将代码块放在with中执行,with结束后,自动完成清理工作,无需手动操作
  • 在需要管理资源如文件、网络、锁的编程中可以在__exit__中定制自动释放资源的机制

一名测试工作者,专注接口测试、自动化测试、性能测试、Python技术。