Дешевая обработка исключений в Python? - PullRequest
28 голосов
/ 28 февраля 2009

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

Я не слышал об этом раньше, но я относительно новичок в Python. Обработка исключений означает динамический вызов и статический возврат, тогда как оператор if - это статический вызов, статический возврат.

Как проверка может быть плохой, а try-except хорошей, кажется, все наоборот. Может кто-нибудь объяснить мне это?

Ответы [ 8 ]

36 голосов
/ 28 февраля 2009

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

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

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

24 голосов
/ 28 февраля 2009

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

Выводы, к которым он пришел:

Исходя из этих результатов, я думаю, что это справедливо быстро определить количество Выводы:

  1. Если существует высокая вероятность того, что элемент не существует, то вам лучше проверить это с has_key.
  2. Если вы не собираетесь ничего делать с Исключением, если оно поднят, значит тебе лучше не положить один, кроме
  3. Если существует вероятность того, что элемент существует, то существует небольшое преимущество использования try / кроме блок вместо использования has_key, однако преимущество очень незначительное.
23 голосов
/ 28 февраля 2009

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

Рассмотрим эти два фрагмента:

# Look before you leap
if not os.path.exists(filename):
    raise SomeError("Cannot open configuration file")
f = open(filename)

против

# Ask forgiveness ...
try:
  f = open(filename)
except IOError:
  raise SomeError("Cannot open configuration file")

Эквивалент? На самом деле, нет. Операционные системы являются мульти-принимающими системами. Что произойдет, если файл был удален между проверкой вызова «Существует» и «Открыт»?

Что произойдет, если файл существует, но он не читается? Что делать, если это имя каталога, а не файла. Возможных режимов сбоя может быть много, и проверка всех из них - большая работа. Тем более, что вызов open уже проверяет и сообщает обо всех возможных сбоях.

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

9 голосов
/ 28 февраля 2009

«Может кто-нибудь объяснить мне это?»

Зависит.

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

"Обработка исключений означает динамический вызов и статический возврат, тогда как оператор if является статическим вызовом, статический возврат."

Что означает «динамический вызов»? Поиск фреймов стека для обработчика? Я предполагаю, что это то, о чем ты говоришь. И «статический вызов» как-то находит блок после оператора if.

Возможно, этот «динамический вызов» не самая дорогостоящая часть операции. Возможно, выражение выражения if-оператора немного дороже, чем простой метод «попробуй-и-провалий».

Оказывается, что внутренние проверки целостности Python почти такие же, как ваш оператор if, и должны быть выполнены в любом случае. Поскольку Python всегда проверяет, ваш оператор if (в основном) избыточен.

Об обработке исключений низкого уровня можно прочитать в http://docs.python.org/c-api/intro.html#exceptions.


Редактировать

Более конкретно: Если против, кроме дебатов не имеет значения.

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

Используйте то, что делает ваш код понятным и значимым . Не тратьте время на такие микрооптимизации.

8 голосов
/ 28 февраля 2009

С Python легко проверить различные возможности по скорости - познакомьтесь с модулем timeit :

... пример сеанса (с использованием командной строки), который сравнивает стоимость использования hasattr () с попыткой /, за исключением проверки отсутствия и представления атрибутов объекта.

% timeit.py 'try:' '  str.__nonzero__' 'except AttributeError:' '  pass'
100000 loops, best of 3: 15.7 usec per loop
% timeit.py 'if hasattr(str, "__nonzero__"): pass'
100000 loops, best of 3: 4.26 usec per loop
% timeit.py 'try:' '  int.__nonzero__' 'except AttributeError:' '  pass'
1000000 loops, best of 3: 1.43 usec per loop
% timeit.py 'if hasattr(int, "__nonzero__"): pass'
100000 loops, best of 3: 2.23 usec per loop

Эти результаты синхронизации показаны в случае hasattr(), вызывая исключение медленно, но выполнение теста медленнее, чем не вызывая исключение. Таким образом, с точки зрения времени выполнения, использование исключения для обработки исключительных случаев имеет смысл.

РЕДАКТИРОВАТЬ: параметр командной строки -n будет по умолчанию достаточно большой счет, так что время выполнения имеет смысл. A цитата из руководства :

Если -n не задано, подходящее количество циклов рассчитывается путем попытки последовательных степеней 10, пока общее время не составит не менее 0,2 секунды.

4 голосов
/ 28 февраля 2009

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

Обратите внимание, что проверка с помощью if-elif-else должна каждый раз оценивать условие. Обработка исключений, включая поиск обработчика исключений, происходит только в исключительном состоянии, которое в большинстве случаев может быть редким. Это явное повышение эффективности. Как отметил Джей, лучше использовать условную логику, а не исключения, когда существует высокая вероятность отсутствия ключа. Это потому, что если ключ отсутствует в большинстве случаев, это не исключительное условие.

Тем не менее, я предлагаю вам не беспокоиться об эффективности, а скорее о смысле. Используйте обработку исключений, чтобы выявлять исключительные случаи и проверять условия, когда вы хотите что-то решить. Мне напомнили о важности значения С.Лотт только вчера.

Показательный пример:

def xyz(key):
   dictOb = {x:1, y:2, z:3}
   #Condition evaluated every time
   if dictOb.has_key(key):  #Access 1 to dict
        print dictOb[key]  #Access 2

Versus

#Exception mechanism is in play only when the key isn't found.
def xyz(key):
   dictOb = {x:1, y:2, z:3}
   try:
        print dictOb[key]  #Access 1
   except KeyError:
        print "Not Found"

В целом, имея некоторый код, который обрабатывает что-то, например, отсутствующий ключ, на всякий случай требует обработки исключений, но в ситуациях, когда ключ отсутствует в большинстве случаев, то, что вы действительно хотите сделать, это решить, если ключ присутствует => if-else. Python подчеркивает и поощряет говорить, что вы имеете в виду.

Почему исключения предпочтительнее if-elif ->

  1. Он более четко выражает значение, когда вы ищете врага исключительный он же необычный / неожиданный случай в вашем коде.
  2. Он чище и намного удобочитаемее.
  3. Он более гибкий.
  4. Может использоваться для написания более краткого кода.
  5. Избегает много неприятных проверок.
  6. Это более ремонтопригодно.

Примечание Когда мы избегаем использования try-исключения, исключения продолжают возникать. Исключения, которые не обрабатываются, просто отправляются в обработчик по умолчанию. Когда вы используете try-exc, вы можете справиться с ошибкой самостоятельно. Это может быть более эффективным, поскольку if-else требует оценки условия, в то время как поиск обработчика исключений может быть дешевле. Даже если это правда, выигрыш от этого будет слишком незначительным, чтобы думать о нем.

Надеюсь, мой ответ поможет.

1 голос
/ 02 марта 2009

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

Эта дискуссия часто называется "LBYL vs EAFP" - это "посмотри, прежде чем прыгнуть" против "проще просить прощения, чем разрешения". Алекс Мартелли рассуждает на эту тему здесь: http://mail.python.org/pipermail/python-list/2003-May/205182.html Этим дебатам уже почти шесть лет, но я не думаю, что основные вопросы сильно изменились.

1 голос
/ 28 февраля 2009

Что такое статические и динамические вызовы и возвраты, и почему вы думаете, что вызовы и возвраты в Python различаются в зависимости от того, выполняете ли вы это в блоке try / Кроме? Даже если вы не перехватываете исключение, Python по-прежнему должен обрабатывать вызов, возможно, вызывающий что-то, поэтому для Python это не имеет значения в отношении того, как обрабатываются вызовы и возвраты.

Каждый вызов функции в Python включает в себя помещение аргументов в стек и вызов вызываемого объекта. За каждым завершением функции следует вызывающая сторона во внутренней проводке Python, проверяющая успешное завершение или завершение исключения, и обрабатывает его соответствующим образом. Другими словами, если вы думаете, что есть некоторая дополнительная обработка, когда вы находитесь в блоке try / Кроме того, что каким-то образом пропускается, когда вы не в одном, вы ошибаетесь. Я предполагаю, что это то, о чем вы отличались от «статического» и «динамического».

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

...