Python3.7学习笔记27-上下文管理器(context manager)

Python3.7学习笔记27-上下文管理器(context manager)

文件的输入输出,数据库的连接与断开,这种是常见的资源管理操作。因为资源有限。在这类场景中。如果在使用过这些资源但是没有得到释放的话。会造成资源泄露。轻者使系统缓慢,重则会使系统奔溃。

一、基于场景

如下作死的代码。我们一次打开100000000个文件。但是只往里面写。而不选择关闭文件。不仅最后会报资源泄露的问题。严重的话会让电脑死机。所以不要执行下面的代码。

# 典型的没做关闭处理。会造成资源泄漏
for x in range(1000000000):
    f = open('test.txt','w')
    f.write('test')


# 大概率会报
OSError:[Errno 23] Too many open........

# 而且你电脑的cpu会狂飙。如果是windows系统或者差一点的。死机也是很正常的。

 

为了解决上面那种有可能人为的出错。在python中引入了上下文管理机制。它能够帮助你自动分配资源并且释放资源。最典型的应用就是with语句。如下上面的代码可以重写。with 语句 在每次执行完成。内部都会相当于f.close() 关闭文件。释放资源。

# 没事也别运行这种代码。不然新建这么多的文件删的蛋疼。测试的话循环次数个位数。
for x in range(100000000):
    with open('test.txt','w') as f:
        f.write('test')

 当然还有一种写法。还记得我们学的异常吧。不管前面是否报错。一定要执行的finally

for x in range(10000000):
    f = open('test.txt', 'w')
    try:
        f.write('hello')
    finally: # try 这里的代码块就算异常了。也会执行关闭文件的操作
        f.close()

但是对比一下代码的简洁度。我们还是倾向于用with 语句。类似其他场景比如数据库 进程锁也是一样的。可以自己试试。

二、基于类的上下文管理

在创建类的上下文管理器的时候,必须保证这个类含有 __enter__()   __exit__()。如下。我们也算了解了with内部逻辑。其实是封装了2个方法。把管理资源的功能都赋予他们。我们只要关心我们自己本身的操作如写入读出等。

class FileManager:
    def __init__(self, name, mode):
        print('初始化类变量')
        self.name = name
        self.mode = mode
        self.file = None

    def __enter__(self):
        """
        返回需要被管理的资源
        :return:
        """
        print('调用:返回需要被管理的资源函数')
        self.file = open(self.name, self.mode)
        return self.file

    def __exit__(self, exc_type, exc_val, exc_tb):
        """
        释放 清理资源的操作。当有异常抛出。会把信息传入到这3个变量中
        :param exc_type:exception_type
        :param exc_val:exception_vale
        :param exc_tb:traceback
        :return:
        """
        print('调用释放资源函数')
        if self.file:
            self.file.close()
        if exc_type:
            print(f'exception_type: {exc_type}') # 以 f开头表示在字符串内支持大括号内的python 表达式
            print(f'exc_value: {exc_val}')
            print(f'exc_traceback: {exc_tb}')


with FileManager('test.txt', 'w') as f:
    print('测试写入文件')
    f.write('我是被写入的文字')
    raise Exception('主动触发异常').with_traceback(None)

如下最常见的除了文件管理还有数据库的连接

import pymysql


class MySql:
    def __init__(self, host, user, password, port, db):
        """
        初始化参数值
        :param host: 地址
        :param user: 账号
        :param password: 密码
        :param port: 端口号
        :param db: 数据库名称
        """
        self.host = host
        self.user = user
        self.password = password
        self.port = int(port)
        self.db = db
        self.value = True

    def __enter__(self):
        """
        连接数据库 返回实例化对象
        :return:
        """
        try:
            self.sqlConn = pymysql.connect(
                host=self.host,
                user=self.user,
                password=self.password,
                db=self.db,
                charset="utf8",
                port=self.port)
            self.sqlCursor = self.sqlConn.cursor()
        except Exception as e:
            self.value = False
            self.err = e
            return self

        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        """
        关闭数据库连接释放资源
        :param exc_type:
        :param exc_val:
        :param exc_tb:
        :return:
        """
        try:
            self.sqlCursor.close()
        except:
            pass

    def select(self,sql):
        """
        查询数据
        :param sql:
        :return: 正常返回=[{},{},{}.....]  异常直接返回错误信息
        """
        if self.value:
            try:
                self.sqlCursor.execute(sql)
                cur = self.sqlCursor.description  # 查询返回值的字段名称
                result = self.sqlCursor.fetchall()  # 查询返回的值
            except BaseException as e:
                return e

            data = []
            # 把查询结果值解析成预期数据类型的格式
            try:
                for i in range(len(result)):
                    lie = {}
                    for j in range(len(cur)):
                        lie[cur[j][0]] = result[i][j]
                    data.append(lie)
            except BaseException as e:
                return e

            return data
        else:
            return self.err

    def update(self,sql):
        """
        更新数据
        :param sql:
        :return: 
        """

        try:
            # 执行SQL语句
            self.sqlCursor.execute(sql)
            # 提交到数据库执行
            self.sqlConn.commit()
        except BaseException as e:
            # 发生错误时回滚
            self.sqlConn.rollback()
            return e

        return

    def insert(self,sql):
        """
        新增数据
        :param sql:
        :return: 
        """
        try:
            self.sqlCursor.execute(sql)
            self.sqlConn.commit()
        except BaseException as e:
            self.sqlConn.rollback()
            return e

        return

 

 

你可能感兴趣的:(python学习笔记,python)