ITPub博客

首页 > 数据库 > 数据库开发技术 > python面向对象编程篇(8):类魔法函数之with语句和__enter__和__exit_关系

python面向对象编程篇(8):类魔法函数之with语句和__enter__和__exit_关系

原创 数据库开发技术 作者:程序员小钟同学 时间:2021-03-15 18:02:50 0 删除 编辑

本文相关描述主要是个人的认知和见解,如有不当之处,还望各位大佬指正。

个人其他博客地址:

简书:https://www.jianshu.com/u/d6960089b087

掘金:https://juejin.cn/user/2963939079225608

小钟同学 | 文 【原创】| QQ:308711822

1、前言

如何有效进行资源管理闭合,自定义上下文管理器对象来帮忙!

2、with在flask中的使用示例

但是在python中类有with语句经常见:如之前我们的flask库自定义的数据库的连接和关闭的时候就用到(这里使用的是装饰器的模式实现):

如下代码:


from contextlib import contextmanager

@contextmanager
def session_scope():
    session = GetSession()
    if session.is_closed():
        # session.connection_context()
        session.connect()
    try:
        yield session
        session.commit()
    except:
        session.rollback()
        raise
    finally:
        session.close()

然后我们使用的时候是直接可以使用:

 with session_scope():
        print([v for v in TestClassId.select().dicts()])
        # 根据主键ID查询
        print(TestClassId.get_by_id(1).test_class)
        # 根据主键ID查询
        print(TestClassId.get_or_none(TestClassId.test_class == 'b').test_class)

3、python中的with作用

with是从Python一个语法糖,它是一种上下文管理协议,目的在于把我们之前常见一个开发 try,except 和finally 关键字和一些文件开关闭合资源分配释放等问题都简化。

总结起来使用python 提供的with主要的作用是:

  • 实现自动调用对象资源的释放
  • 异常捕获和回滚
  • 减少用户手动调用的方法

常见的几个应用场景有:

  • 1、文件操作。
  • 2、进程线程之间互斥对象。
  • 3、支持上下文其他对象
  • 4、需要进行资源链接和结束的时候进行释放操作
  • 5、释放锁
  • 6、创建代码补丁

如之前我们的一个打开文件和关闭文件的操作:

没有使用with:

# 完成一个文件的额读取操作
f1 = open('tx1.txt', 'r',encoding='utf-8')
data = f1.read()
print(data)
# 关闭文件
f1.close()

使用with:

with open('tx1.txt', 'r') as f1:
    print(f1.read())

使用with即时再读取文件的过程中出现了异常,也能正常的关闭我们的f1.close()

5、with工作原理

本质上其实with是上下文管理:

  • 1、上下文管理协议 :
    • 包含方法__enter__() 和 exit(),支持该协议对象要实现这两个方法。
  • 2、上下文管理器 :
    • 定义执行with语句时要建立的运行时上下文,负责执行with语句块上下文中的进入与退出操作。
  • 3、进入上下:
    • 执行__enter__方法
  • 4、运行时发生了异常或结束是:
    • 退出上下文管理器,调用管理器__exit__方法。

6、使用类的方法实现with示例

类需要实现:

  • __ enter__(self):
  • __ exit__(self,exc_type,exc_value,traceback):

__ exit__()方法的3个参数,分别代表异常的类型、值、以及堆栈信息。

无异常的示例:


class ConmenManage(object):
    def __init__(self,name):
        self.name=name
    def __enter__(self):
        print("进入enter")
        return self

    def say(self):
        print(self.name)

    def __exit__(self,exc_type,exc_value,traceback):
        print("退出exit")
        print(exc_type,exc_value)

if __name__ == '__main__':
    with ConmenManage('小钟同学') as xiaozhong:
        xiaozhong.say()

输出的结果:

进入enter
小钟同学
退出exit
None None

异常的情况下:


class ConmenManage(object):
    def __init__(self,name):
        self.name=name
    def __enter__(self):
        print("进入enter")
        return self

    def say(self):
        3534/0
        print(self.name)

    def __exit__(self,exc_type,exc_value,traceback):
        print("退出exit")
        print(exc_type,exc_value)

if __name__ == '__main__':
    with ConmenManage('小钟同学') as xiaozhong:
        xiaozhong.say()

输出:

进入enter
退出exit
Traceback (most recent call last):
<class 'ZeroDivisionError'> division by zero
  File "D:/code/python/local_python/AntFlask/ceshi3.py", line 22, in <module>
    xiaozhong.say()
  File "D:/code/python/local_python/AntFlask/ceshi3.py", line 13, in say
    3534/0
ZeroDivisionError: division by zero

在异常的情况也下也能做的进入到我们的__exit__执行我们的相关的异常的处理。

PS:exit() 方法中对变量exc_trackback进行检测,如果不为 None,表示发生了异常,返回 False 表示需要由外部代码逻辑对异常进行处理;

with语句的执行过程总结:

  • 1、调用__enter__方法
  • 2、执行内部方法
  • 3、调用__exit__方法   进行相关的资源释放或关闭等都可以再这里处理

with ConmenManage('小钟同学') as xiaozhong 中的xiaozhong

上面的xiaozhong是一种with语句的变量定义的方式,xiaozhong它是一个临时变量:甚至支持多变量如:


class ConmenManage(object):
    def __init__(self,name):
        self.name=name
    def __enter__(self):
        print("进入enter")
        return self

    def say(self):
        print(self.name)

    def __exit__(self,exc_type,exc_value,traceback):
        print("退出exit")
        print(exc_type,exc_value)

if __name__ == '__main__':
    with ConmenManage('小钟同学') as xiaozhong,ConmenManage('小小同学') as xiaoxiao:
        xiaozhong.say()
        xiaoxiao.say()

输出的结果为:

进入enter
进入enter
小钟同学
小小同学
退出exit
None None
退出exit

所以多变量的情况,其实就是一种内嵌with的实现:

 with ConmenManage('小钟同学') as xiaozhong,ConmenManage('小小同学') as xiaoxiao:
        xiaozhong.say()
        xiaoxiao.say()

和

    with ConmenManage('小钟同学') as xiaozhong:
        with ConmenManage('小小同学') as xiaoxiao:
            xiaozhong.say()
            xiaoxiao.say()

一个关于数据库简单的使用类的方式封装的示例:

class PgHelper(object):
    """操作postgresql数据库的类"""

    def __init__(self, db):
        self.connect = None
        self.cursor = None
        # 初始化数据库参数
        self.db_name = db.get('db_name', '')
        self.db_user = db.get('db_user', '')
        self.db_pass = db.get('db_pass', '')
        self.db_host = db.get('db_host', '')
        self.db_port = db.get('db_port', '')

    def __enter__(self):
        """初始化数据库链接"""
        self.open_conn()
        return self

    def __exit__(self, type, value, trace):
        """关闭postgresql数据库链接"""
        self.close_conn()

    def open_conn(self):
        """连接数据库,并建立游标"""
        try:
            if not self.connect:
                self.connect = psycopg2.connect(database=self.db_name, user=self.db_user, password=self.db_pass,
                                                host=self.db_host, port=self.db_port)
            return self.connect
        except Exception as e:
            logger.error('连接数据库失败:' + str(e.args))
            # print('连接数据库失败:' + str(e.args))
            return False

    def close_conn(self):
        """关闭postgresql数据库链接"""
        # 关闭游标
        try:
            if self.cursor:
                self.cursor.close()
        except Exception:
            pass
        # 关闭数据库链接
        try:
            if self.connect:
                self.connect.close()
        except Exception:
            pass

    def rollback(self):
        """回滚操作"""
        try:
            # 操作异常时,操作回滚
            if self.connect:
                self.connect.rollback()
                self.close_conn()
        except Exception as e:
            logger.error('回滚操作失败:' + str(e.args))

    def commit(self):
        """提交事务"""
        try:
            if self.connect:
                self.connect.commit()
                self.close_conn()
        except Exception as e:
            logger.error('提交事务失败:' + str(e.args))

7、装饰器的方法实现with示例

代码示例:


from contextlib import contextmanager

@contextmanager
def session_scope():
    session = GetSession()
    if session.is_closed():
        # session.connection_context()
        session.connect()
    try:
        yield session
        session.commit()
    except:
        session.rollback()
        raise
    finally:
        session.close()

我们上面通过装饰器的形式进行数据库的链接,并且再装饰器的内部返回一个生成器对象。(关于生成器下次再说明一下),在执行内部过程如果出现了异常,则进行事务的回滚,不管最终处理怎么样,我们的会进行相关的链接的关闭。

参考资料:

https://www.jianshu.com/p/5b01fb36fd4c

以上是个人对Python的with语句用法一些简要阐述。

End

纯属个人实践中相关经验阐述总结,如有纰漏,还希望大佬们多多提点!小钟同学 | 文  【原创】| QQ:308711822


原文作者:程序员小钟同学;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论

注册时间:2021-02-20

  • 博文量
    224
  • 访问量
    111722