Анти-паттерны Python

Переусердствовать, кроме пункта

Исключения являются мощными, но одно чрезмерное усердие, за исключением предложения, может убрать все это в одну строку.

try:
    res = get_result()
    res = res[0]
    log('got result: %r' % res)
except:
    if not res:
        res = ''
    print('got exception') 

Этот пример демонстрирует 3 симптома антипаттерна:

  1. За except без указания типа исключения (строка 5) будет ловить даже здоровые исключения, в том числе KeyboardInterrupt.Это предотвратит выход из программы в некоторых случаях.
  2. Кроме блока не ререйз ошибки, а это означает , что мы не сможем сказать , если исключение пришло в get_result или потому , что res был пустой список.
  3. Хуже всего то, что если мы беспокоились о том, что результат окажется пустым, мы стали причиной чего-то гораздо худшего. Если 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) , однако многие обстоятельства могут потребовать более сложные операции , которые необходимо сделать в случае, когда ключ нет