首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >为什么MySQLdb连接上下文管理器不关闭光标?

为什么MySQLdb连接上下文管理器不关闭光标?
EN

Stack Overflow用户
提问于 2015-07-13 11:05:06
回答 1查看 3.1K关注 0票数 19

MySQLdb Connections有一个基本的上下文管理器,它可以在回车时创建光标,在退出时回滚或提交,并且隐式地不会抑制异常。从Connection source

代码语言:javascript
复制
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

代码语言:javascript
复制
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中的垃圾收集了解不多。

代码语言:javascript
复制
def __del__(self):
    self.close()
    self.errorhandler = None
    self._result = None

另一种猜测是,可能存在一种情况,即您希望在with块之后重用游标。但我想不出任何理由让你这么做。难道您不能总是在游标的上下文中使用完游标,然后在下一个事务中使用单独的上下文吗?

需要非常清楚的是,这个例子显然没有意义:

代码语言:javascript
复制
with conn as cursor:
    cursor.execute(select_stmt)

rows = cursor.fetchall()

它应该是:

代码语言:javascript
复制
with conn as cursor:
    cursor.execute(select_stmt)
    rows = cursor.fetchall()

这个例子也没有意义:

代码语言:javascript
复制
# 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()

它应该是:

代码语言:javascript
复制
# first transaction
with conn as cursor:
    cursor.execute(update_stmt_1)

# second transaction, new cursor
with conn as cursor:
    cursor.execute(update_stmt_2)

再说一遍,在退出时关闭光标有什么坏处,不关闭它有什么好处?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-07-29 19:57:16

直接回答您的问题:我看不出在with代码块的末尾关闭有什么坏处。我不能说为什么在这种情况下不这样做。但是,由于在这个问题上缺乏活动,我搜索了一下代码历史,并将抛出一些想法(猜测)来解释为什么close() 不能调用

  1. 在对nextset()的调用中旋转可能会抛出异常的可能性很小-这可能已经被观察到并被视为不受欢迎的情况。这可能是newer version of cursors.pyclose()中包含此结构的原因:

def close(self):“关闭游标。不能进行进一步的查询。”如果不是这样,self.connection: return self._flush() try: while self.nextset():pass except: pass self.connection =None

  • 可能需要一些时间来遍历所有剩余的结果,而不做任何事情。因此,可能不会调用close()来避免进行一些不必要的迭代。我想,你是否认为节省这些时钟周期是值得的,这是主观的,但你可以沿着“如果没有必要,就不要做”的思路进行辩论。

  • 浏览sourceforge提交,该功能是由this commit在2007年添加到主干中的,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__()语法,例如

代码语言:javascript
复制
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块的末尾没有对象的引用。

票数 11
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31374857

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档