какой стиль предпочтительнее? - PullRequest
9 голосов
/ 22 января 2010

Вариант 1:

def f1(c):
  d = {
    "USA": "N.Y.",
    "China": "Shanghai"
  }

  if c in d:
    return d[c]

  return "N/A"

Вариант 2:

def f2(c):
  d = {
    "USA": "N.Y.",
    "China": "Shanghai"
  }

  try:
    return d[c]
  except:
    return "N/A"

Так что я могу позвонить:

for c in ("China", "Japan"):
  for f in (f1, f2):
    print "%s => %s" % (c, f(c))

Возможны следующие варианты: определить, находится ли ключ в каталоге до раздачи (f1), или просто вернуться к исключению (f2). Какой из них предпочтительнее? Почему?

Ответы [ 7 ]

21 голосов
/ 22 января 2010

Ни я бы не пошел за

def f2(c):
  d = {
    "USA": "N.Y.",
    "China": "Shanghai"
  }

  return d.get(c, "N/A")

Этот способ короче, и "get" предназначен для работы.

Кроме того, исключение без явного исключения является плохой практикой, поэтому используйте except KeyError: не только для исключения.

Исключения в Python не слишком загружены. Как правило, лучше использовать их, если нет лучших альтернатив или иногда даже когда это сохраняет поиск атрибутов (использовать вместо hasattr).

Редактировать: , чтобы прояснить общую мысль об исключениях.

paxdiablo правильно в общем. Python в основном предназначен для «проще просить прощения, чем разрешения», то есть попробуйте потом посмотреть, что не получилось (исключения), затем «посмотрите, прежде чем прыгнуть», посмотрите, что там, а затем примените. Это потому, что поиск атрибутов в python может быть дорогим, поэтому повторный вызов одного и того же материала (для проверки границ) является пустой тратой ресурсов. Тем не менее, у внутреннего содержимого в Python обычно есть более приятные помощники, поэтому их лучше использовать.

11 голосов
/ 22 января 2010

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

Если вы не изменяете ассоциативный массив вне текущего потока, тогда вы можете использовать метод "check-first-then-extract".

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

return d.get (c, "N/A")

Я поясню, что я сказал в первом абзаце. В ситуациях, когда базовые данные могут меняться между проверкой и использованием, вы должны всегда использовать операцию типа исключения (если у вас нет операции, которая не вызовет проблемы, такой как d.get(), упомянутая выше) , Рассмотрим, например, следующие две временные линии потока:

+------------------------------+--------------------+
| Thread1                      | Thread2            |
+------------------------------+--------------------+
| Check is NY exists as a key. |                    |
|                              | Delete NY key/val. |
| Extract value for NY.        |                    |
+------------------------------+--------------------+

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

Комментарий к базам данных также актуален, поскольку это другая ситуация, когда базовые данные могут измениться. Вот почему я предпочитаю атомарный SQL (где это возможно), а не что-то вроде получения списка ключей с последующей обработкой их с помощью отдельных операторов.

9 голосов
/ 22 января 2010

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

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

7 голосов
/ 22 января 2010

Ни.

return d.get(c, 'N/A')
4 голосов
/ 22 января 2010

Я с Дэвидом на этом:

def f2(c):
   d = {
        "USA": "N.Y.",
        "China": "Shanghai"
       }

   return d.get(c, "N/A")

... именно так я и написал бы.

Чтобы указать другие варианты:

В 'f1 ()', в этом нет ничего плохого как такового, но в словарях есть метод get () для почти такого точного варианта использования: "получить это из dict, а если его нет, вернуть другая вещь вместо этого ". Это все, что говорит ваш код, использование get () более кратко.

В 'f2 ()', используя «кроме» сам по себе, как это осуждается, и, кроме того, вы действительно ничего не делаете в ответ на исключение - в вашем случае вызывающий код никогда не узнает было исключение. Так зачем использовать конструкцию, если она не добавляет ценности вашей функции или коду, который ее вызывает?

1 голос
/ 22 января 2010

Я вижу людей, использующих "get", которые я рекомендую. Однако, если вы столкнетесь с подобной ситуацией в будущем, поймайте исключение, которое вы имеете в виду:

try:
    return d[k]
except KeyError:
    return "N/A"

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

0 голосов
/ 22 января 2010

Я согласен, что в этом случае dict.get является лучшим решением.

В общем, я думаю, ваш выбор будет зависеть от того, насколько вероятны исключения. Если вы ожидаете, что ключевые поиски в основном пройдут, тогда попытка / отлов - лучший выбор IMO. Точно так же, если они будут часто терпеть неудачу, оператор if лучше.

Производительность исключений по сравнению с поиском по атрибутам в Python не столь различна, поэтому я бы больше беспокоился о логике использования исключений / просмотра перед вами, чем об аспектах производительности.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...