python 'with', следует ли использовать contextlib.closing?

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()?

Когда я узнал о с утверждением, многие статьи говорит, что она закрывает файл автоматически после того, как процесс, как показано ниже ( так же, как, наконец: 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()

Ответ 1

Да, вы должны использовать context.closing(); ваша собственная версия делает что-то совершенно другое.

Оператор with позволяет менеджеру контекста знать, когда блок кода вводится и завершается; при выходе менеджер контекста также получает доступ к исключению, если это произошло. Файловые объекты используют это для автоматического закрытия файла при выходе из блока.

Функция connect_db() из учебника возвращает sqlit3 соединения sqlit3, который действительно может использоваться в качестве менеджера контекста. Однако метод 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__() вызывается при выходе из блока.

Ответ 2

Единственное, что делается с with оператора with - это вызвать метод __enter__ перед тем, как ввести его блок и __exit__ прежде чем выйти из него. Если эти методы не определены, оператор with не будет работать так, как вы можете ожидать. Я не знаю, что такое тип возврата connect_db, но я думаю, что это может быть много разных вещей из разных сторонних библиотек. Таким образом, ваш код без closing, вероятно, будет работать во многих случаях (все?), Но вы никогда не знаете, что может быть возвращено connect_db.