Python с использованием исключений для потока управления считается плохой?

Хорошо,

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

Если это так плохо для всех, кто использует python, почему язык включает его в то, что считается фундаментальной структурой управления? Для тех, кто хочет прочитать соответствующий PEP перейти сюда.

Ответы

Ответ 1

Потому что окончание генератора не является общим событием (я знаю, что это всегда произойдет, но это произойдет только один раз). Выбрасывание исключения считается дорогостоящим. Если событие преуспеет в 99% случаев и сработает 1%, использование try/except может быть намного быстрее, чем проверка того, можно ли получить доступ к этим данным (проще попросить прощения, чем разрешение).

Там также есть смещение против него, поскольку try/except используемые таким образом блоки могут быть очень трудными для понимания. Контроль потока может быть затруднен, в то время как if/else более прост. Функция try/except означает, что вы должны отслеживать управление потоком операторов внутри try и внутри функций, которые он вызывает (поскольку они могут генерировать исключение и могут распространяться вверх). If/else может только веткиться в точке, когда утверждение.

Бывают случаи, когда использование try/except правильное и время, когда if/else имеет больше смысла. Также есть затраты на производительность, связанные с каждым из них. Рассмотрим:

a = <some dictionary>
if key in a:
    print a[key]

против.

a = <some dictionary>
try:
    print a[key]
except KeyError:
    pass

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

РЕДАКТИРОВАТЬ: Просто немного, чтобы добавить о попытке Python/кроме того, что очень помогает в решении одной из проблем чтения.

Рассмотрите возможность чтения из файла.

f = None
try:
    f = open(filename, 'r')
    ... do stuff to the file ...
except (IOError, OSError):
    # I can never remember which one of these Python throws...
    ... handle exception ...
finally:
    if f:
        f.close()

Теперь что-нибудь в do stuff to the file может выдать исключение, и мы его поймаем. Как правило, вы пытаетесь сохранить как можно меньше кода в попытке по этой причине. Python имеет необязательное предложение else для try, которое будет запускаться только в том случае, если попытка завершилась до завершения без исключения.

f = None
try:
    f = open(filename, 'r')
except (IOError, OSError):
    pass
else:
    ... do stuff to the file ...
finally:
    if f:
        f.close()

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

Ответ 2

Использование блоков try для управления потоком все время может генерировать код следующим образом:

try:
  # stuff
  try:
    if userCondition:
      throw NeedToDoSomethingElseException
    try:
      # stuff
    except NeedToDoSomethingElseException:
      # other stuff
  except NeedToDoSomethingElseException:
    # other stuff
except NeedToDoSomethingElseException:
  # other stuff

Относительно производительности, это не очень элегантно. Таким образом, иногда идеально подходит для использования try, но не для всего.

Ответ 3

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