关于Python上下文管理器__enter__、__exit__与mysql.connector使用疑问
原代码错误点说明
- 你把连接逻辑写到了单独的
co()方法里,co()没有返回值,所以with后面调用co()拿到的是None,无法赋值给log __enter__方法是空的,没有返回任何内容,就算不调用co(),as后面的变量也是None__exit__方法没有写关闭连接的逻辑,会导致连接泄漏self.raws = self.crouser.execute(self.que)这里赋值是无效的,execute不会返回查询结果,fetchall()的返回值才是你要的目标数据
问题解答
1. 查询结果的返回位置
查询执行和结果获取的逻辑应该放到__enter__方法中,__enter__的返回值会直接赋值给with语句as后面的变量,也就是你要的查询结果。
2. 数据库连接的关闭时机
__exit__方法是with代码块执行结束后(哪怕中间代码报错)一定会触发的逻辑,你要在这个方法里依次关闭游标、关闭数据库连接,就能保证连接不会泄露。
3. with语法的正确使用方式
with后面直接跟你定义的类实例即可,不需要额外调用自定义方法,实例进入with上下文时会自动触发__enter__的逻辑,退出时自动触发__exit__的逻辑。
修正后的完整代码
import mysql.connector class Tak: def __init__(self, host1, user1, password1, db1, aat, que1): self.host = host1 self.user = user1 self.password = password1 self.database = db1 self.auth_plugin = aat self.que = que1 # 提前初始化连接和游标变量,避免报错时属性不存在 self.conn = None self.cursor = None def __enter__(self): try: # 初始化数据库连接 self.conn = mysql.connector.connect( host=self.host, user=self.user, password=self.password, database=self.database, auth_plugin=self.auth_plugin, charset='utf8' ) self.cursor = self.conn.cursor() # 执行查询 self.cursor.execute(self.que) # 返回查询结果,直接赋值给as后的变量 return self.cursor.fetchall() except mysql.connector.Error as err: print(f"数据库操作出错:{err}") # 出错返回空避免赋值报错 return [] def __exit__(self, exc_type, exc_val, exc_tb): # 不管with块内是否报错,都关闭资源 if self.cursor: self.cursor.close() if self.conn: self.conn.close() # 返回False代表不吞掉with块内的异常,有错误会正常抛出 return False # 正确的with调用方式 with Tak( host1="localhost", user1="root", password1="1234", db1="local_db", aat='mysql_native_password', que1='select * from nand' ) as log: print(log)
额外注意事项
- 类名建议遵循Python大驼峰命名规范,不要用全小写的命名,可读性更高
mysql.connector.errors是异常模块,捕获异常的时候要写mysql.connector.Error才是正确的异常类- 如果后续要做增删改操作,需要在
__exit__里判断有没有异常,没有异常就调用self.conn.commit()提交事务,纯查询场景不需要提交
内容的提问来源于stack exchange,提问作者Mark
相关产品推荐
相关产品推荐

