Как «с» лучше, чем попытаться / поймать, чтобы открыть файл в Python? - PullRequest
40 голосов
/ 08 января 2012

Я понял, что оператор with поможет вам перевернуть это:

try:
    f = open(my_file)
    do_stuff_that_fails()
except:
    pass
finally:
    f.close()

В:

with open(my_file) as f:
    do_stuff_that_fails()

Но как это лучше? Вы все еще должны обработать дело с файлом, который не может быть открыт (например, предложить пользователю сказать ему, что у него нет разрешений), так что в действительности у вас будет:

try:
    with open(my_file) as f:
        do_stuff_that_fails()
except (IOError, OSError, Failure) as e:
    do_stuff_when_it_doesnt_work()

Что эквивалентно:

try:
    f = open(my_file)
    do_stuff_that_fails()
except (IOError, OSError, Faillure) as e:
    do_stuff_when_it_doesnt_work()
finally:
    f.close()

Да, вы получили две строки, но вы добавили уровень вложенности, который не облегчает чтение. Цель оператора with - сохранить две строки или я что-то упустил?

Кажется, что для этого нужно добавить ключевое слово, поэтому я чувствую, что есть некоторый синтаксис для обработки дополнительной попытки / за исключением того, о чем я не знаю.

Ответы [ 3 ]

34 голосов
/ 08 января 2012

Для начала, это поможет предотвратить проблему, которую вы представили в своем примере try ... finally ....

Как вы его структурировали, если при попытке открыть файл выдается исключение, вы никогда не будете связывать открытый файл с именем f, что приведет к NameError в предложении finally (если f никогда не был связан в рамках) или что-то совершенно неожиданное (если это было).

Правильная структура (эквивалентная with):

f = open(my_file)

try:
    do_stuff_that_fails()
finally:
    f.close()

(примечание - нет необходимости в выражении except, если вам нечего там делать).

Ваш второй пример также неверен и должен иметь следующую структуру:

try:
    f = open(my_file)

    try:
        do_stuff_that_fails()
    except EXPECTED_EXCEPTION_TYPES as e:
        do_stuff_when_it_doesnt_work()
    finally:
        f.close()

except (IOError, OSError) as e:
    do_other_stuff_when_it_we_have_file_IO_problems()

Второй (как указано в другом ответе), что вы не можете забыть позвонить f.close().

Кстати, термин «управление контекстом», а не «управление ресурсами» - оператор with управляет контекстами , некоторые из которых могут быть ресурсами, но другие - нет. Например, он также используется с decimal для установки десятичного контекста для определенного блока кода.

Наконец (отвечая на ваш комментарий к предыдущему ответу), вы никогда не должны полагаться на семантику refcount для обработки ресурсов в Python. Jython, IronPython и PyPy имеют семантику не-refcount, и ничто не мешает CPython пойти другим путем (хотя в ближайшем будущем это маловероятно). В замкнутом цикле (например, os.walk) очень легко выйти из файловых дескрипторов, если код, основанный на семантике refcount, выполняется на ВМ с другим поведением.

16 голосов
/ 08 января 2012

В приведенном вами примере это не лучше.Рекомендуется отлавливать исключения как можно ближе к той точке, в которую они брошены, чтобы избежать перехвата несвязанных исключений одного и того же типа.

try:
    file = open(...)
except OpenErrors...:
    # handle open exceptions
else:
    try:
        # do stuff with file
    finally:
        file.close()

К сожалению, столь многословно, как оператор with не позволяет вам ловить исключения, сгенерированные во время его оценки.Было предложение добавить обработку исключений для этого эффекта в список рассылки:

with open(...) as file:
    # do stuff with file
except OpenErrors...:
    # handle open exceptions

Но это было сбито .

НаконецСтоит отметить, что вы можете напрямую входить и выходить из контекстных менеджеров, например, так:

file = open(...).__enter__()
file.__exit__(typ, val, tb)

Это более подробно описано здесь и здесь .

Как общее правило, операторы with превосходны для случаев, когда исключения не ожидаются, и поведение по умолчанию "ввод / открытие / приобретение" является адекватным.Примеры включают необходимые файлы и простую блокировку.

7 голосов
/ 08 января 2012

Это для управления ресурсами ... не для того, как вы реагируете на исключение иначе:)

Нет способа "забыть" f.close() при использовании with,Таким образом, он выполняет ту же роль, что и using в C #.

Счастливое кодирование.

...