В Python, если я вернусь внутрь блока «with», файл все еще закроется? - PullRequest
196 голосов
/ 27 марта 2012

Рассмотрим следующее:

with open(path, mode) as f:
    return [line for line in f if condition]

Будет ли файл закрыт должным образом или с помощью return каким-либо образом обойдется менеджер контекста ?

Ответы [ 4 ]

179 голосов
/ 27 марта 2012

Да, он действует как блок finally после блока try, то есть он всегда выполняется (если, конечно, процесс python не завершается необычным образом).

Это также упоминается в одном из примеров PEP-343 , который является спецификацией для оператора with:

with locked(myLock):
    # Code here executes with myLock held.  The lock is
    # guaranteed to be released when the block is left (even
    # if via return or by an uncaught exception).

Однако стоит упомянуть, что вы не можете легко перехватывать исключения, генерируемые вызовом open(), не помещая весь блок with в блок try..except, что обычно не то, что нужно.

29 голосов
/ 27 марта 2012

Да.

def example(path, mode):
    with open(path, mode) as f:
        return [line for line in f if condition]

.. в значительной степени эквивалентно:

def example(path, mode):
    f = open(path, mode)

    try:
        return [line for line in f if condition]
    finally:
        f.close()

Точнее, метод __exit__ в диспетчере контекста всегда вызывается при выходе из блока (независимо от исключений, возвратов и т. Д.). Метод __exit__ объекта файла просто вызывает f.close() (например, здесь, в CPython )

15 голосов
/ 08 октября 2016

Да.В более общем смысле метод __exit__ для с менеджером контекста оператора действительно будет вызываться в случае return из контекста.Это можно проверить с помощью следующего:

class MyResource:
    def __enter__(self):
        print('Entering context.')
        return self

    def __exit__(self, *exc):
        print('EXITING context.')

def fun():
    with MyResource():
        print('Returning inside with-statement.')
        return
    print('Returning outside with-statement.')

fun()

Вывод:

Entering context.
Returning inside with-statement.
EXITING context.

Вывод выше подтверждает, что __exit__ был вызван, несмотря на ранний return.Таким образом, менеджер контекста не обойден.

3 голосов
/ 02 октября 2018

Да, но в других случаях может быть некоторый побочный эффект, потому что он может что-то делать (например, очистка буфера) в __exit__ блоке

import gzip
import io

def test(data):
    out = io.BytesIO()
    with gzip.GzipFile(fileobj=out, mode="wb") as f:
        f.write(data)
        return out.getvalue()

def test1(data):
    out = io.BytesIO()
    with gzip.GzipFile(fileobj=out, mode="wb") as f:
        f.write(data)
    return out.getvalue()

print(test(b"test"), test1(b"test"))

# b'\x1f\x8b\x08\x00\x95\x1b\xb3[\x02\xff' b'\x1f\x8b\x08\x00\x95\x1b\xb3[\x02\xff+I-.\x01\x00\x0c~\x7f\xd8\x04\x00\x00\x00'
...