【问题标题】:In Python with sqlite is it necessary to close a cursor?在带有 sqlite 的 Python 中是否有必要关闭游标?
【发布时间】:2011-01-20 18:46:02
【问题描述】:

这里是场景。在您的函数中,您正在使用游标执行语句,但其中一个失败并引发异常。您的程序在关闭它正在使用的光标之前退出函数。光标会飘来飘去占用空间吗?我必须关闭光标吗?

此外,Python 文档有一个游标使用示例,并说:“如果我们完成了游标,我们也可以关闭它。”关键字是“可以”,而不是“必须”。这究竟是什么意思?

【问题讨论】:

    标签: python sqlite


    【解决方案1】:

    这可能是一个好主意(虽然它可能与 sqlite 无关,不知道那里,但它会让你的代码更具可移植性)。此外,使用最近的 Python (2.5+),这很容易:

    from __future__ import with_statement
    from contextlib import closing
    
    with closing(db.cursor()) as cursor:
        # do some stuff
    

    【讨论】:

      【解决方案2】:

      您不必在光标上调用close();它可以像任何其他对象一样被垃圾收集。

      但是,即使等待垃圾回收听起来不错,我认为确保数据库游标等资源无论是否出现异常都被关闭仍然是一种不错的方式。

      【讨论】:

        【解决方案3】:

        有趣的是,Python 3.0 doc 说“如果我们完成了光标,我们也可以关闭它”,而 Python 2.73.6 文档说“我们也可以关闭 连接如果我们完成了它”。

        Python 2.7 和 3.0-3.4 文档没有描述游标 .close() 方法。但是 Python 3.5 和 3.6 文档描述了 cursor .close() 方法:

        立即关闭光标(而不是在每次调用 __del__ 时)。

        从现在开始,光标将无法使用;如果尝试对游标进行任何操作,将引发ProgrammingError 异常。

        【讨论】:

          【解决方案4】:

          全部,

          我在使用 sqlite3 的代码 (Python 3.8) 中遇到了逐渐的内存泄漏。我将可能的原因追溯到我的数据库类。事实证明,我会打开并使用游标,但从不关闭它。数据库在程序(Windows 服务)的生命周期内保持打开状态,并在退出时关闭。

          一旦我开始关闭所有使用游标的数据库操作中的游标,我的内存泄漏就停止了,并且内存占用变得稳定。

          因此,我建议您花时间关闭光标。它使代码更加一致,显然有助于控制内存消耗。

          以下是我如何关闭光标的示例:

          def write_to_db(self, cache_item:CacheEntry):
                  '''Write a single cache entry to the database'''
                  crsr = self._db_con.cursor()
          
                  # Load some data elements
                  fax_line_path = cache_item._dir_part
                  phone_line = cache_item._phone_line
                  sub_folder = cache_item._subfolder
                  fname = cache_item._fname
                  work_done = cache_item.get_workdone()
          
                  try:
                      crsr.execute(FilenameCacheDB.INSERT_CACHE,
                                       (fax_line_path, 
                                        phone_line, 
                                        sub_folder, 
                                        fname, 
                                        work_done))
          
                  except Exception as e:
                      LOG.warning(f"Could not write {cache_item} to db because {e}")
                      raise e
          
                  finally:
                      #
                      # I was *not* closing the cursor prior
                      #
                      crsr.close()
                      self._db_con.commit()
          

          【讨论】:

            【解决方案5】:

            我还没有看到sqlite3.Cursor.close() 操作的任何效果。

            关闭后,您仍然可以调用fetch(all|one|many),它将返回上一个执行语句的剩余结果。即使运行Cursor.execute() 仍然有效...

            【讨论】:

            • 我注意到了同样的行为(我编写了一个测试以确保光标已关闭,但它失败了),并想知道这是 python 连接器问题还是 sqlite3 固有的问题。
            【解决方案6】:

            此代码将自动关闭Cursor。它还会自动关闭并提交Connection

            import sqlite3
            import contextlib
            
            def execute_statement(statement):
                with contextlib.closing(sqlite3.connect(path_to_file)) as conn: # auto-closes
                    with conn: # auto-commits
                        with contextlib.closing(conn.cursor()) as cursor: # auto-closes
                            cursor.execute(statement)
            

            【讨论】:

            • 为什么我不能只使用with con.cursor() as cursor: ...?那会更容易阅读..这段代码感觉有点像用java编程..嗯/shrug
            【解决方案7】:

            看代码sn-p和stackoverflowuser2010和Peer给出的思路,使用Python contextmanager优雅地处理游标更容易。

            from contextlib import contextmanager
            
            @contextmanager
            def OpenCursor(conn):
                cursor = conn.cursor()
                try:    
                    yield (cursor)
                except Exception as e:  
                    cursor.close()  
                    raise e
                else:                     
                    cursor.close() 
            

            在没有 OpenCursor 的情况下使用:

            def get(conn, key, default=None):
                cursor = conn.cursor()
                cursor.execute(f'SELECT value FROM table WHERE key=?', (key,))
                row = cursor.fetchone()
                if row:
                    return (True)
                else:
                    return (default)
            

            使用 OpenCursor 作为上下文管理器:

            def get(conn, key, default=None):
                with OpenCursor(conn) as cursor:
                    cursor.execute(f'SELECT value FROM table WHERE key=?', (key,))
                    row = cursor.fetchone()
                    if row:
                        return (True)
                    else:
                        return (default)
            

            【讨论】:

              猜你喜欢
              • 2018-08-23
              • 1970-01-01
              • 1970-01-01
              • 2019-05-20
              • 2021-12-08
              • 2010-12-31
              • 2013-05-14
              • 1970-01-01
              • 2012-04-28
              相关资源
              最近更新 更多