Python Language
Python Anti-Patterns
Поиск…
Переусердствовать, кроме оговорки
Исключения являются мощными, но одна чрезмерно усредненная фраза может занять все это в одной строке.
try: res = get_result() res = res[0] log('got result: %r' % res) except: if not res: res = '' print('got exception')
Этот пример демонстрирует 3 симптома антипаттера:
- За
except
без указания типа исключения (строка 5) будет ловить даже здоровые исключения, в том числеKeyboardInterrupt
. Это предотвратит выход из программы в некоторых случаях. - Блок except не регрессирует ошибку, что означает, что мы не сможем определить, произошло ли исключение из
get_result
или потому, чтоres
был пустым. - Хуже всего, если бы мы были обеспокоены тем, что результат был пуст, мы привели к чему-то значительно худшему. Если
get_result
не удался,res
останется полностью отключенным, а ссылка наres
вNameError
блоке повысит значение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)
, однако многим обстоятельствам может потребоваться выполнение более сложных операций в случае, если ключ отсутствует