Переусердствовать, кроме пункта
Исключения являются мощными, но одно чрезмерное усердие, за исключением предложения, может убрать все это в одну строку.
try:
res = get_result()
res = res[0]
log('got result: %r' % res)
except:
if not res:
res = ''
print('got exception')
Этот пример демонстрирует 3 симптома антипаттерна:
- За
except
без указания типа исключения (строка 5) будет ловить даже здоровые исключения, в том числеKeyboardInterrupt
.Это предотвратит выход из программы в некоторых случаях. - Кроме блока не ререйз ошибки, а это означает , что мы не сможем сказать , если исключение пришло в
get_result
или потому , чтоres
был пустой список. - Хуже всего то, что если мы беспокоились о том, что результат окажется пустым, мы стали причиной чего-то гораздо худшего. Если
get_result
не удается,res
будет оставаться полностью снято с охраной, а также ссылка наres
в блоке , за исключением, подниметNameError
, полностью маскирует первоначальную ошибку.
Всегда думайте о типе исключения, которое вы пытаетесь обработать. Дайте странице Исключения для чтения и получить ощущение того, что существуют основные исключения.
Вот исправленная версия примера выше:
import traceback
try:
res = get_result()
except Exception:
log_exception(traceback.format_exc())
raise
try:
res = res[0]
except IndexError:
res = ''
log('got result: %r' % res)
Мы ловим более конкретные исключения, ререйзируем при необходимости. Еще несколько строк, но бесконечно правильнее.
Глядя, прежде чем прыгнуть с интенсивной процессорной функцией
Программа может легко тратить время, многократно вызывая функцию с интенсивным использованием процессора.
Например, возьмем функцию , которая выглядит следующим образом : он возвращает целое число , если входное value
не может производить один, еще None
:
def intensive_f(value): # int -> Optional[int]
# complex, and time-consuming code
if process_has_failed:
return None
return integer_output
И это можно использовать следующим образом:
x = 5
if intensive_f(x) is not None:
print(intensive_f(x) / 2)
else:
print(x, "could not be processed")
print(x)
Несмотря на то , что это будет работать, то есть проблема вызова intensive_f
, которая удваивает продолжительность времени код для запуска. Лучшим решением было бы получить возвращаемое значение функции заранее.
x = 5
result = intensive_f(x)
if result is not None:
print(result / 2)
else:
print(x, "could not be processed")
Тем не менее, более четкий и , возможно , более вещий способ заключается в использовании исключений, например:
x = 5
try:
print(intensive_f(x) / 2)
except TypeError: # The exception raised if None + 1 is attempted
print(x, "could not be processed")
Здесь никакая временная переменная не требуется. Это часто может быть предпочтительнее использовать assert
заявление, и поймать AssertionError
вместо этого.
Словарь ключей
Типичным примером того, где это может быть найдено, является доступ к словарным ключам. Например сравнить:
bird_speeds = get_very_long_dictionary()
if "european swallow" in bird_speeds:
speed = bird_speeds["european swallow"]
else:
speed = input("What is the air-speed velocity of an unladen swallow?")
print(speed)
с:
bird_speeds = get_very_long_dictionary()
try:
speed = bird_speeds["european swallow"]
except KeyError:
speed = input("What is the air-speed velocity of an unladen swallow?")
print(speed)
В первом примере необходимо дважды просмотреть словарь, и, поскольку это длинный словарь, это может занять много времени каждый раз. Второе требует только одного поиска в словаре и, таким образом, экономит много процессорного времени.
Альтернативой этому является использование dict.get(key, default)
по dict.get(key, default)
, однако многие обстоятельства могут потребовать более сложные операции , которые необходимо сделать в случае, когда ключ нет