python'with'语句,我应该使用contextlib.closing吗?

时间:2013-12-27 00:57:47

标签: python with-statement contextmanager

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 语句时,许多文章说它会在下面的过程之后自动关闭文件。(与Last: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()

2 个答案:

答案 0 :(得分:17)

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

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

教程中的connect_db()函数返回sqlit3 connection object,它确实可以用作context manager但是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__()方法。

答案 1 :(得分:2)

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