【问题标题】:python 'with' statement, should I use contextlib.closing?python 'with' 语句,我应该使用 contextlib.closure 吗?
【发布时间】:2013-12-27 00:57:46
【问题描述】:
from contextlib import closing

def init_db():
    with closing(connect_db()) as db:
        with app.open_resource('schema.sql') as f:
            db.cursor().executescript(f.read())
        db.commit()

这是来自烧瓶教程第 3 步(http://flask.pocoo.org/docs/tutorial/dbinit/#tutorial-dbinit)。我对第 4 行有点好奇。

我必须导入并使用那个 'contextlib.closing()' 方法吗?

当我了解 with 语句时,很多文章都说它在处理后自动关闭文件,如下所示。(与 Final:thing.close() 相同)

with open('filename','w') as f:
    f.write(someString);

即使我没有像下面那样使用 contextlib.closing(),有什么区别? 是2.7.6版的,谢谢。

def init_db():
    with connect_db() as db:
        with app.open_resource('schema.sql') as f:
            db.cursor().executescript(f.read())
        db.commit()

【问题讨论】:

    标签: python with-statement contextmanager


    【解决方案1】:

    是的,您应该使用context.closing();您自己的版本完全不同。

    with 语句让上下文管理器知道何时输入和退出代码块;退出时,上下文管理器也可以访问异常(如果发生)。文件对象使用它在块退出时自动关闭文件。

    教程中的connect_db() 函数返回一个sqlite3 connection object,它确实可以用作context manager但是connection.__exit__() 方法不会关闭连接,它在成功完成时提交事务,或者在出现异常时中止它。

    另一方面,contextlib.closing() 上下文管理器在连接上调用 connection.close() 方法。这是完全不同的

    因此,您的第二个 sn-p 可能会起作用,但会有所不同。教程代码关闭连接,您的版本提交事务。您已经在调用db.commit(),因此如果没有引发异常,该操作实际上是多余的。

    您可以再次将连接用作上下文管理器以具有自动事务处理行为:

    def init_db():
        with closing(connect_db()) as db:
            with app.open_resource('schema.sql') as f, db:
                db.cursor().executescript(f.read())
    

    注意第二个with 行上的, db,确保在块退出时调用db.__exit__() 方法。

    【讨论】:

    • 我不太明白你的最后一句话(关于“,db”)。你能详细说明这是如何工作的吗?当外部上下文(关闭...)完成时,数据库是否关闭?为什么要将 db 拉到内部嵌套上下文中?
    • @DavidBarker: with closing(connect_db()) as db 做了两件事:它创建了一个上下文管理器,在退出时将在connect_db() 结果上调用close()并且它返回相同的结果来自cm.__enter__() 方法的对象,因此with 将其分配给名称db。现在,该对象本身也是上下文管理器,所以在第二个with中,我们确保with也调用db.__enter__()db.__exit__(),因为那些控制交易。
    • 所以closing()上下文管理器调用close()而不调用__enter__()__exit__()(即使对象本身就是一个上下文管理器)在街区的尽头?这很有意义 - 感谢您抽出宝贵时间回复对这么旧帖子的评论。
    【解决方案2】:

    with 语句所做的唯一事情是在进入其块之前调用 __enter__ 方法和在退出它之前调用 __exit__ 方法。 如果未定义这些方法,with 语句将无法正常工作。我不知道connect_db 的返回类型是什么,但我想这可能是来自不同第三方库的许多不同的东西。因此,没有closing 的代码可能会在许多(所有?)情况下工作,但你永远不知道connect_db 可以返回什么。

    【讨论】:

      猜你喜欢
      • 2011-11-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-12
      • 2010-09-07
      相关资源
      最近更新 更多