[python] 讲讲上下文管理器

什么是上下文管理器

简单来说,上下文管理器的目的就是规定对象的使用范围,如果超出范围就采取相应“处理”;比如:

f = open('filename.txt')
data = f.read()
f.close()

打开一个文件,读取文件内容,关闭文件。正常的业务中,这样写是不够的,因为在操作资源的时候有可能出现错误,为了加强代码健壮性,需要经常改为:

try:
    f = open('filename.txt')
    data = f.read()
except:
    pass
finally:
    f.close()

不管出现什么错误,最后都要关闭和释放资源f.close(),为了代码更具可读性,且不容易出错,就会用with实现上下文管理器:​​​​​​​

with open('filename.txt') as f:
    data = f.read()

先理清几个概念:​​​​​​​

1. 上下文表达式:with open('filename.txt') as f:2. 上下文管理器:open('filename.txt')3. f 不是上下文管理器,应该是资源对象,是上下文管理器返回的对象

实现上下文管理器

要自己实现这样一个上下文管理,要先知道上下文管理协议。简单点说,就是在一个类里,实现了__enter__和__exit__的方法,这个类的实例就是一个上下文管理器。​​​​​​​

class MyResource:
    def __enter__(self):
        print('opening resource......')
        return self

    def operate(self):
        print('doing something......')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('closing resource......')


with MyResource() as r:
    r.operate()

当打开资源的时候进入__enter__方法,return的对象会赋给as对象,即这里return回self对象会赋值给r;不管有没有出错,执行完with代码块后都会进入到__exit__方法,如果出错,__exit__的参数就会被赋值。

业务实战

实现一个连接mysql数据库的上下文管理器,就以上面的代码为基础​​​​​​​

import pymysql


class MyResource:
    
    def __init__(self, database):
        self.database = database
    
    def __enter__(self):
        self.conn = pymysql.connect(
            host='localhost',
            port=3306,
            user='root',
            password='xxxxxx',
            database=self.database,
            charset='utf8'
        )
        self.cursor = self.conn.cursor()
        return self.cursor

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.conn.commit()
        self.cursor.close()
        self.conn.close()


with MyResource('datatbase') as db:
    db.execute('update test set name="rhys" where id=1')

用with实例化MyResource对象,进入__enter__函数连接数据库,返回游标cursor给db,db.execute进行update操作,在退出with代码块的时候进入__exit__函数,进行commit操作再关闭游标和连接。

contextmanager实现上下文管理器

还有另一种方式实现一个上下问管理器,就是用contextlib里的contextmanager装饰器:​​​​​​​

from contextlib import contextmanager
import pymysql


@contextmanager
def mysqldb(database):
    try:
        conn = pymysql.connect(
            host='localhost',
            port=3306,
            user='root',
            password='xxxxxx',
            database=database,
            charset='utf8'
        )
        cursor = conn.cursor()
        yield cursor
        conn.commit()
    except Exception as e:
        print(e)
    finally:
        cursor.close()
        conn.close()


with mysqldb('database') as db:
    db.execute('update test set name="rhys" where id=1')

被contextmanager装饰的函数会成为一个上下文管理器,用yield返回cursor给as后面的db,执行完update操作退出with代码块的时候再回到yield的位置,执行之后的代码。

个人更喜欢使用contexmanager来实现上下问管理器,代码更简便更容易编写,也更好理解。

 

关注公众号:日常bug,适合技术点滴积累,利用琐碎时间学习技术的人。

### PyCharm 打开文件显示全的解决方案 当遇到PyCharm打开文件显示全的情况时,可以尝试以下几种方法来解决问题。 #### 方法一:清理缓存并重启IDE 有时IDE内部缓存可能导致文件加载异常。通过清除缓存再启动程序能够有效改善此状况。具体操作路径为`File -> Invalidate Caches / Restart...`,之后按照提示完成相应动作即可[^1]。 #### 方法二:调整编辑器字体设置 如果是因为字体原因造成的内容显示问题,则可以通过修改编辑区内的文字样式来进行修复。进入`Settings/Preferences | Editor | Font`选项卡内更改合适的字号大小以及启用抗锯齿功能等参数配置[^2]。 #### 方法三:检查项目结构配置 对于某些特定场景下的源码视图缺失现象,可能是由于当前工作空间未能正确识别全部模块所引起。此时应该核查Project Structure的Content Roots设定项是否涵盖了整个工程根目录;必要时可手动添加遗漏部分,并保存变更生效[^3]。 ```python # 示例代码用于展示如何获取当前项目的根路径,在实际应用中可根据需求调用该函数辅助排查问题 import os def get_project_root(): current_file = os.path.abspath(__file__) project_dir = os.path.dirname(current_file) while not os.path.exists(os.path.join(project_dir, '.idea')): parent_dir = os.path.dirname(project_dir) if parent_dir == project_dir: break project_dir = parent_dir return project_dir print(f"Current Project Root Directory is {get_project_root()}") ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值