为什么MySQLdb Connection上下文管理器不关闭游标? [英] Why doesn't the MySQLdb Connection context manager close the cursor?

查看:86
本文介绍了为什么MySQLdb Connection上下文管理器不关闭游标?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

MySQLdb Connections具有一个基本的上下文管理器,可在 enter 上创建一个游标,回滚或在 exit 上提交,并且隐式不抑制异常.从连接源:

MySQLdb Connections have a rudimentary context manager that creates a cursor on enter, either rolls back or commits on exit, and implicitly doesn't suppress exceptions. From the 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()

那么,有谁知道为什么退出时光标未关闭?

So, does anyone know why the cursor isn't closed on exit?

起初,我以为是因为关闭游标并没有做任何事情,并且游标只具有close方法,以符合此答案的评论).但是,事实是关闭游标会消耗掉剩余的结果集(如果有的话)并禁用游标.从光标源:

At first, I assumed it was because closing the cursor didn't do anything and that cursors only had a close method in deference to the Python DB API (see the comments to this answer). However, the fact is that closing the cursor burns through the remaining results sets, if any, and disables the cursor. From the 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的垃圾回收了解不多.

It would be so easy to close the cursor at exit, so I have to suppose that it hasn't been done on purpose. On the other hand, we can see that when a cursor is deleted, it is closed anyway, so I guess the garbage collector will eventually get around to it. I don't know much about garbage collection in Python.

def __del__(self):
    self.close()
    self.errorhandler = None
    self._result = None


另一个猜测是,在某些情况下,您可能想在with块之后重新使用光标.但我想不出您为什么需要这样做的任何理由.您不能总是在光标的上下文内完成光标的使用,而只对下一个事务使用单独的上下文吗?


Another guess is that there may be a situation where you want to re-use the cursor after the with block. But I can't think of any reason why you would need to do this. Can't you always finish using the cursor inside its context, and just use a separate context for the next transaction?

非常清楚,此示例显然没有意义:

To be very clear, this example obviously doesn't make sense:

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)

再次,关闭退出时的游标有什么危害,不关闭它有什么好处?

推荐答案

直接回答您的问题:在with块结尾处关闭时,我看不到任何危害.我不能说为什么在这种情况下不这样做.但是,由于该问题缺乏活动,因此我在代码历史中进行了搜索,并提出了一些关于close()的想法( guesses ). > 可能 不会被调用:

To answer your question directly: I cannot see any harm whatsoever in closing at the end of a with block. I cannot say why it is not done in this case. But, as there is a dearth of activity on this question, I had a search through the code history and will throw in a few thoughts (guesses) on why the close() may not be called:

  1. 很少有机会通过调用nextset()进行旋转,可能会引发异常-可能已经观察到并认为这是不希望的.这可能就是为什么较新版本的cursors.py 包含在内的原因close()中的这种结构:

  1. There is a small chance that spinning through calls to nextset() may throw an exception - possibly this had been observed and seen as undesirable. This may be why the newer version of cursors.py contains this structure in close():

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

  • 存在(某种程度的远程)潜力,可能需要花费一些时间来浏览所有其余结果,无所作为.因此,可能不会调用close()以避免进行一些不必要的迭代.我想,您是否认为值得节省这些时钟周期是主观的,但是您可以按照如果没有必要,就不要这样做"的观点进行争论.

  • There is the (somewhat remote) potential that it might take some time to spin through all the remaining results doing nothing. Therefore close() may not be called to avoid doing some unnecessary iterations. Whether you think it's worth saving those clock cycles is subjective, I suppose, but you could argue along the lines of "if it's not necessary, don't do it".

    浏览sourceforge提交,该功能已通过此提交添加到了主干 a>在2007年,看来connections.py的此部分此后没有更改.这是基于此提交(其中包含消息

    Browsing the sourceforge commits, the functionality was added to the trunk by this commit in 2007 and it appears that this section of connections.py has not changed since. That's a merge based on this commit, which has the message

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

    您引用的代码从那以后再也没有改变过.

    And the code you quote has never changed since.

    这引起了我的最终想法-这可能只是第一次尝试/原型,它只是起作用了,因此从未改变过.

    This prompts my final thought - it's probably just a first attempt / prototype that just worked and therefore never got changed.


    更现代的版本

    您链接到源的旧版连接器.我注意到在此处处,有一个更活跃的同一个库,我在有关较新版本"的评论中链接了该库.在第1点.


    More modern version

    You link to source for a legacy version of the connector. I note there is a more active fork of the same library here, which I link to in my comments about "newer version" in point 1.

    请注意,此模块的最新版本已在cursor本身中实现了__enter__()__exit__():调用self.close() ,也许这提供了一种更标准的方式来使用with语法,例如

    Note that the more recent version of this module has implemented __enter__() and __exit__() within cursor itself: see here. __exit__() here does call self.close() and perhaps this provides a more standard way to use the with syntax e.g.

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


    尾注

    NB 我想我应该补充一句,据我所知,一旦没有引用conn的地方,垃圾收集(也不是专家)将会是释放.此时,将没有对游标对象的引用,它也将被释放.


    End notes

    N.B. I guess I should add, as far as I understand garbage collection (not an expert either) once there are no references to conn, it will be deallocated. At this point there will be no references to the cursor object and it will be deallocated too.

    但是 调用cursor.close()并不意味着将被垃圾回收.它只是烧录结果并将连接设置为None.这意味着它不能被重复使用,但是不会立即被垃圾回收.您可以通过在with块之后手动调用cursor.close()然后说出打印cursor

    However calling cursor.close() does not mean that it will be garbage collected. It simply burns through the results and set the connection to None. This means it can't be re-used, but it won't be garbage collected immediately. You can convince yourself of that by manually calling cursor.close() after your with block and then, say, printing some attribute of cursor

    N.B. 2 我认为这是with语法的一种不寻常用法,因为conn对象仍然存在,因为它已经在外部范围内了-例如,与更常见的with open('filename') as f:with块结束之后,没有任何对象随引用一起徘徊.

    N.B. 2 I think this is a somewhat unusual use of the with syntax as the conn object persists because it is already in the outer scope - unlike, say, the more common with open('filename') as f: where there are no objects hanging around with references after the end of the with block.

    这篇关于为什么MySQLdb Connection上下文管理器不关闭游标?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆