изменения внутри try-кроме сохраняются после того, как исключение поймано - PullRequest
8 голосов
/ 20 января 2011



Поскольку я впервые узнал об обработке исключений (не в Python), у меня сложилось впечатление, что когда вы начинаете блок try , вы начинаете писать в песочнице: если возникает исключение все, что произошло внутри блока try , будет похоже на то, что никогда не происходило. К моему наивному удивлению, я заметил, что это не так, или не так, как я думал, по крайней мере, в Python. Вот мой эксперимент на Python:

>>> a = range(5)
>>> a
[0, 1, 2, 3, 4]
>>> try:
...     a.append(5)
...     oops
... except:
...     raise
... 
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
NameError: name 'oops' is not defined
>>> print a
[0, 1, 2, 3, 4, 5]

Как видите, я изменил список внутри блока try , затем вызвал ошибку, которая возникла. Я ожидал увидеть список в его первоначальном виде, [0, 1, 2, 3, 4], но a.append(5) сохранился.

Были ли мои ожидания неверными с самого начала? Может быть, частично неверные ожидания (может быть песочница, но она так не действует)?

Ответы [ 4 ]

6 голосов
/ 21 января 2011

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

Исключения не защищают вас от изменений состояния ... все, что было выполнено без ошибок до возникновения исключения, должно быть отменено. Именно так работают исключения в Python, C ++, Java и многих других языках.

Иногда у вас может быть своего рода «внешняя» общая защита: например, если все, что вы делаете, это изменения в базе данных, поддерживающей транзакции, то вы можете использовать оператор перехвата верхнего уровня для выполнения «отката» вместо фиксации меняется, и вы получаете защиту, которую вы ищете. Если нет такой естественной «стены», защищающей от частичного изменения состояния, то с вещами гораздо сложнее правильно обращаться.

Сама причина того, что завершенные операции не будут отменены, состоит в том, что делает использование исключений нетривиальным, поскольку сложность проблем возрастает.

Обычно код можно классифицировать как исключение «безопасный» на нескольких уровнях:

  1. В случае исключения все разрушается, и даже чистый выход или перезапуск невозможны. Это то, что обычно классифицируется как НЕ безопасное исключение.

  2. В случае исключения код не завершит свою работу, и состояние подсистемы (экземпляр класса, библиотека) будет недопустимым. Однако вы можете безопасно перезапустить (например, вы можете уничтожить экземпляр или повторно инициализировать библиотеку). Это самое минимальное исключение безопасности.

  3. В случае исключения код не завершит свою работу, и состояние подсистемы будет действительным, но не определенным. Вызывающий код может попытаться проверить текущее состояние и продолжать использовать подсистему, например, вместо ее повторной инициализации. Чуть лучше, чем 2.

  4. В случае исключения код ничего не сделает, оставив состояние программы без изменений. Таким образом, либо запрос завершен без ошибок, либо сигнал об ошибке возвращается вызывающей стороне, и ничего не было изменено. Это, конечно, лучшее поведение.

Самая большая проблема с обработкой исключений состоит в том, что даже если у вас есть две очень безопасные части типа 4 A и B, простая последовательная композиция AB не безопасна, поскольку в случае проблемы в B Вы также должны отменить все, что A завершил. Также, если возможно получить исключение при выполнении 1/A (т.е. при невыполнении того, что A было в состоянии завершить), тогда у вас большие проблемы, потому что вы не можете ни сделать B, ни восстановить состояние как это было раньше (это означает, что просто невозможно реализовать AB как операцию типа 4).

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

2 голосов
/ 20 января 2011

Да, ваши предположения были неверны.Однако, одна хорошая вещь, которую вы можете сделать, это очистить определенные типы, используя with.См. Учебник Python .

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

2 голосов
/ 20 января 2011

Ваши ожидания неверны. Возможно, это была бы хорошая функция (она существует под транзакциями имен - например, в SQL - но это редко встречается в языках программирования, и большинство исследований происходит на языках, которые никто никогда не использует), но это невозможно на любом языке, который кто-либо использует (гораздо менее эффективно - поскольку компилятор / интерпретатор, как правило, очень мало знает о том, что делает программист, вам придется сохранить все состояние программы и восстановить его, и при этом все равно будут отсутствовать все побочные эффекты за пределами интерпретатора, например, файловый ввод-вывод).

Блок try просто означает " попытайтесь сделать это, и переходите к выполнению этого , если вы потерпите неудачу". Обратите внимание, что только вторая половина является особенной - если исключение происходит за пределами блока try, выполнение перепрыгивает граф вызовов до следующего try, или - если его нет - к некоторому глобальному обработчику, который печатает исключение и завершает выполнение программы.

2 голосов
/ 20 января 2011

Ваше ожидание неверно практически для любого языка, который поддерживает исключения - семантика "все или ничего" не связана с блоками try (хотя в некоторых языках могут существовать транзакционные концепции, например, если есть поддержка транзакционной памяти)

Это только то, что часть блока try после исключения больше не выполняется.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...