27
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()
4

2 回答 2

31

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

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

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

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

因此,您的第二个片段可能会起作用,但会有所不同。教程代码关闭连接,您的版本提交事务。您已经在调用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())

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

于 2013-12-27T01:10:48.927 回答
3

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

于 2013-12-27T01:07:35.167 回答