В простых случаях лучше исключать и восстанавливать или избегать исключения? - PullRequest
0 голосов
/ 31 января 2011

Рассмотрим:

categories = {'foo':[4], 'mer':[2, 9, 0]}

key = 'bar'
value = 5

Мы могли бы безопасно добавить список, сохраненный в словаре, одним из следующих способов:

  1. Будучи осторожными, мы всегда проверяемсуществует ли список до добавления в него.

    if not somedict.has_key(key):
        somedict[key] = []
    somedict[key].append(value)
    
  2. Будучи прямым, мы просто очищаемся, если есть исключение.

    try:
        somedict[key].append(value)
    except KeyError:
        somedict[key] = [value]
    

В обоих случаях результат может быть следующим:

{'foo':[4], 'mer':[2, 9, 0], 'bar':[5]}

Повторю мой вопрос: в таких простых случаях, как это, лучше (с точки зрения стиля, эффективности и философии) быть осторожными или прямыми

Ответы [ 7 ]

3 голосов
/ 31 января 2011

Что вы обнаружите, так это то, что ваш вариант 1 «быть осторожным» часто очень медленный.Кроме того, он подвержен неясным ошибкам, потому что тест, который вы пытались написать, чтобы «избежать» исключения, был неправильным.

Вы обнаружите, что ваш вариант 2 «быть прямым» часто намного быстрее.Это также более вероятно, будет правильным, а также быстрее и легче для людей читать.

Почему?Внутренне, Python часто реализует такие вещи, как «contains» или «has_key» в качестве теста исключения.

def has_key( self, some_key ):
    try:
        self[some_key]
    except KeyError:
        return False
    return True

Так как обычно это метод типа has_key, то нет причин для того, чтобы код делалтратить время на это в дополнение к тому, что Python уже сделает.

Более фундаментально, есть проблема правильности.Многие попытки предотвратить или избежать исключения являются неполными.

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

try:
    x= float( some_string )
except ValueError: 
    # not a floating-point value

Просто сделайте алгоритм, не беспокоясь о «предотвращении» или «избегании» исключений.

2 голосов
/ 31 января 2011

В общем случае, EFAP («проще просить прощения, чем разрешения») предпочтительнее в Python.Конечно, эмпирическое правило «исключения должны быть в исключительных случаях» остается в силе (если вы ожидаете, что исключение будет происходить часто, вы, вероятно, должны «посмотреть, прежде чем прыгнуть») - т.е.это зависит.С точки зрения эффективности, в большинстве случаев это не должно иметь большого значения - если это так, учтите, что try блоки без исключений дешевы, а условия всегда проверяются.

Обратите внимание, что ни в этом нет необходимости (по крайней мере, вам не нужно делать это самостоятельно / явно) в некоторых случаях, включая этот пример - здесь вы должны просто использовать collections.defaultdict

1 голос
/ 31 января 2011

try достаточно быстро, except (если это произойдет) может не быть.Если средняя длина этих списков составит 1,1, используйте метод check-first.Если это будет в тысячах, используйте try / исключением.Если вы действительно обеспокоены, сравните альтернативы.

Убедитесь, что вы сравниваете лучшие альтернативы.d.has_key(k) медленный старый has_been;вам не нужен поиск атрибутов и вызов функции.Используйте k in d вместо этого.Также используйте else для сохранения потерянного append в первой поездке:

Вместо:

if not somedict.has_key(key):
    somedict[key] = []
somedict[key].append(value)

сделайте это:

if key in somedict:
    somedict[key].append(value)
else:
    somedict[key] = [value]
1 голос
/ 31 января 2011

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

  • Версия исключения требует более простого API. Будет работать любой контейнер, который поддерживает поиск и назначение элементов (__getitem__ и __setitem__). Версия без исключения дополнительно требует реализации has_key.
  • Версия исключения может быть немного быстрее, если ключ обычно существует, так как для этого требуется только один запрос dict. Для версии has_key требуется как минимум два - один для has_key и один для фактического поиска.
  • Версия без исключения имеет более согласованный путь кода: она всегда помещает значение в массив в одно и то же место. Для сравнения версия исключения имеет отдельный путь к коду для каждого случая.

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

0 голосов
/ 31 января 2011

EFAP - хорошая привычка для Python.

Одна из причин заключается в том, что он избегает состояния гонки, если кто-то хочет использовать ваш код в многопоточном приложении

0 голосов
/ 31 января 2011

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

0 голосов
/ 31 января 2011

Вы можете использовать setdefault для этого конкретного случая:

somedict.setdefault(key, []).append(value)

Смотрите здесь: http://docs.python.org/library/stdtypes.html#mapping-types-dict

...