MySQLdb Connections
有一个基本的上下文管理器,它可以在回车时创建光标,在退出时回滚或提交,并且隐式地不会抑制异常。从Connection source
def __enter__(self):
if self.get_autocommit():
self.query("BEGIN")
return self.cursor()
def __exit__(self, exc, value, tb):
if exc:
self.rollback()
else:
self.commit()
那么,有没有人知道为什么在退出时游标没有关闭?
起初,我认为这是因为关闭游标没有任何作用,并且游标只有一个Python DB API方法(参见对this answer的注释)。然而,事实是关闭游标会烧遍剩余的结果集(如果有的话),并禁用游标。从cursor source
def close(self):
"""Close the cursor. No further queries will be possible."""
if not self.connection: return
while self.nextset(): pass
self.connection = None
在退出时关闭光标非常容易,所以我必须假设这不是故意的。另一方面,我们可以看到,当一个游标被删除时,它无论如何都会被关闭,所以我猜垃圾收集器最终会绕过它。我对Python中的垃圾收集了解不多。
def __del__(self):
self.close()
self.errorhandler = None
self._result = None
另一种猜测是,可能存在一种情况,即您希望在with
块之后重用游标。但我想不出任何理由让你这么做。难道您不能总是在游标的上下文中使用完游标,然后在下一个事务中使用单独的上下文吗?
需要非常清楚的是,这个例子显然没有意义:
with conn as cursor:
cursor.execute(select_stmt)
rows = cursor.fetchall()
它应该是:
with conn as cursor:
cursor.execute(select_stmt)
rows = cursor.fetchall()
这个例子也没有意义:
# first transaction
with conn as cursor:
cursor.execute(update_stmt_1)
# second transaction, reusing cursor
try:
cursor.execute(update_stmt_2)
except:
conn.rollback()
else:
conn.commit()
它应该是:
# first transaction
with conn as cursor:
cursor.execute(update_stmt_1)
# second transaction, new cursor
with conn as cursor:
cursor.execute(update_stmt_2)
再说一遍,在退出时关闭光标有什么坏处,不关闭它有什么好处?
发布于 2015-07-29 19:57:16
直接回答您的问题:我看不出在with
代码块的末尾关闭有什么坏处。我不能说为什么在这种情况下不这样做。但是,由于在这个问题上缺乏活动,我搜索了一下代码历史,并将抛出一些想法(猜测)来解释为什么close()
不能调用:
nextset()
的调用中旋转可能会抛出异常的可能性很小-这可能已经被观察到并被视为不受欢迎的情况。这可能是newer version of cursors.py
在close()
中包含此结构的原因:def close(self):“关闭游标。不能进行进一步的查询。”如果不是这样,self.connection: return self._flush() try: while self.nextset():pass except: pass self.connection =None
close()
来避免进行一些不必要的迭代。我想,你是否认为节省这些时钟周期是值得的,这是主观的,但你可以沿着“如果没有必要,就不要做”的思路进行辩论。
connections.py
的这一部分似乎从那以后就没有改变过。这是一个基于this commit的合并,其中包含消息添加Python-2.5对with语句的支持,如http://docs.python.org/whatsnew/pep-343.html请测试中所述
你引用的代码从那以后就再也没有改变过。
这提醒了我最后的想法--这可能只是第一次尝试/原型,只是工作而已,因此从来没有改变过。
更现代的版本
您可以链接到旧版连接器的源代码。我注意到同一个库here有一个更活跃的分支,我在第一点中关于“较新版本”的评论中链接到这个分支。
请注意,此模块的较新版本在cursor
本身中实现了__enter__()
和__exit__()
:see here。这里做了 call self.close()
,也许这提供了一种更标准的方式来使用__exit__()
语法,例如
with conn.cursor() as c:
#Do your thing with the cursor
结束语
N.B.我想我应该补充一句,据我所知,垃圾收集(也不是专家)一旦没有引用conn
,它就会被释放。此时,将不会有对游标对象的引用,它也将被释放。
然而,调用cursor.close()
的并不意味着它将被垃圾回收。它只是简单地遍历结果并将连接设置为None
。这意味着它不能被重用,但也不会立即被垃圾回收。您可以通过在with
块之后手动调用cursor.close()
,然后打印cursor
的一些属性来说服自己
N.B2我认为这是with
语法的一种不同寻常的用法,因为conn
对象持续存在,因为它已经在外部作用域中-不像更常见的with open('filename') as f:
,在with
块的末尾没有对象的引用。
https://stackoverflow.com/questions/31374857
复制相似问题