为什么MySQLdb Connection上下文管理器不关闭游标?

时间:2015-07-13 03:05:06

标签: python mysql mysql-python contextmanager

MySQLdb GImageDerivativeOps.sobel(grey, derivX, derivY, BorderType.EXTENDED); GImageDerivativeOps.hessianSobel(derivX, derivY, derivXX, derivXY, derivYY, BorderType.EXTENDED); 有一个基本的上下文管理器,可以在 enter 上创建游标,在 exit 上回滚或提交,并且隐式不会禁止异常。来自Connection source

Connections

那么,有没有人知道为什么光标在退出时没有关闭?

首先,我认为这是因为关闭光标没有做任何事情,并且游标只有一个与Python DB API相关的密切方法(参见对this answer的评论)。但是,事实是关闭光标会烧掉剩余的结果集(如果有),并禁用光标。来自cursor 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中的垃圾收集知之甚少。

def close(self):
    """Close the cursor. No further queries will be possible."""
    if not self.connection: return
    while self.nextset(): pass
    self.connection = None

另一个猜测是,您可能希望在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()

同样,在退出时关闭光标会有什么危害,有什么好处不关闭呢?

1 个答案:

答案 0 :(得分:10)

直接回答您的问题:在with区块结束时,我无法看到任何伤害。我不能说为什么在这种情况下没有这样做。但是,由于在这个问题上缺乏活动,我对代码历史进行了搜索,并会对{{strong> 猜测 >提出一些想法{{strong> 猜测 1}} 可以 不被调用:

  1. 旋转调用close()的可能性很小,可能会引发异常 - 可能这已被观察到并被视为不合需要。这可能是newer version of cursors.pynextset()中包含此结构的原因:

    close()
  2. 可能需要花费一些时间才能完成所有剩余的结果。因此,可能不会调用def close(self): """Close the cursor. No further queries will be possible.""" if not self.connection: return self._flush() try: while self.nextset(): pass except: pass self.connection = None 来避免进行一些不必要的迭代。我认为,你是否认为值得保存这些时钟周期是主观的,但你可以按照“如果没有必要,不要这样做”的方式进行争论。

  3. 浏览sourceforge提交时,该功能已于2007年this commit添加到主干中,此后close()的此部分似乎没有更改。这是基于this commit的合并,其中包含消息

      

    http://docs.python.org/whatsnew/pep-343.html 中所述,为with语句添加Python-2.5支持请测试

    你引用的代码从那时起就没有改变过。

    这促使我最后的想法 - 它可能只是第一次尝试/原型刚刚起作用,因此从未改变过。

  4. 更现代的版本

    您链接到旧版连接器的源。我注意到同一个库here有一个更活跃的分支,我在第1点的关于“更新版本”的评论中链接到这个分支。

    请注意,此模块的更新版本已在connections.py__enter__()内实施__exit__()cursorsee here__exit__()此处 call self.close()并且这可能提供了一种使用with语法的更标准方法,例如。

    with conn.cursor() as c:
        #Do your thing with the cursor
    

    结束说明

    NB 我想我应该添加,据我所知,垃圾收集(不是专家)一旦没有conn的引用,它将被解除分配。此时将不会引用游标对象,它也将被解除分配。

    然而 调用cursor.close()并不意味着它将被垃圾回收。它只是烧掉结果并将连接设置为None。这意味着它无法重复使用,但不会立即进行垃圾回收。您可以通过在cursor.close()阻止后手动调用with,然后打印cursor

    的某些属性来说服自己

    <强> N.B。 2 我认为这是with语法的一种不寻常的用法,因为conn对象仍然存在,因为它已经在外部范围内了 - 不像比较常见with open('filename') as f: with块结束后没有任何对象与引用挂在一起的<resources> <!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/blue</item> <item name="colorButtonNormal">@color/blue</item> </style> </resources>