Python: если ключ в dict против try / except

у меня есть вопрос об идиомах и удобочитаемости, и, похоже, существует столкновение философий Python для этого конкретного случая:

Я хочу построить словарь A из словаря B. Если конкретный ключ не существует в B, то ничего не делайте и продолжайте.

какой путь лучше?

try:
    A["blah"] = B["blah"]
except KeyError:
    pass

или

if "blah" in B:
    A["blah"] = B["blah"]

"делай и проси прощения "против" простоты и эксплицитности".

что лучше и почему?

9 ответов


исключения не являются условными.

условная версия понятнее. Это естественно: это простое управление потоком, для которого предназначены условия, а не исключения.

версия исключения в основном используется в качестве оптимизации при выполнении этих поисков в цикле: для некоторых алгоритмов она позволяет исключить тесты из внутренних циклов. Здесь у него нет такого преимущества. У него есть небольшое преимущество, что он избегает говорить "blah" в два раза, но если вы делаете много из них, вы, вероятно, должны иметь помощника move_key в любом случае функция.

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


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

value = B.get("blah", None)
if value is None: 
    A["blah"] = value

в случае, если вы ожидаете, что словарь должен содержать None значения, вы можете использовать некоторые более эзотерические константы, как NotImplemented, Ellipsis или:

MyConst = object()
def update_key(A, B, key):
    value = B.get(key, MyConst)
    if value is not MyConst: 
        A[key] = value

в любом случае, используя update() является наиболее читаемым вариантом для меня:

a.update((k, b[k]) for k in ("foo", "bar", "blah") if k in b)

из того, что я понимаю,вы хотите обновить dict A с помощью ключа, пары значений из dict B

update лучший выбор.

A.update(B)

пример:

>>> A = {'a':1, 'b': 2, 'c':3}
>>> B = {'d': 2, 'b':5, 'c': 4}
>>> A.update(B)
>>> A
{'a': 1, 'c': 4, 'b': 5, 'd': 2}
>>> 

прямая цитата из Python performance wiki:

за исключением первого раза, каждый раз, когда слово видно, тест оператора if терпит неудачу. Если вы считаете большое количество слов, многие из них, вероятно, произойдут несколько раз. В ситуации, когда инициализация значения происходит только один раз, а увеличение этого значения будет происходить много раз, дешевле использовать оператор try.

Так кажется, что оба варианта жизнеспособны в зависимости от ситуации. Для получения более подробной информации вы можете проверить эту ссылку:Try-except-performance


Я думаю, что второй пример-это то, что вы должны пойти, если этот код имеет смысл:

try:
    A["foo"] = B["foo"]
    A["bar"] = B["bar"]
    A["baz"] = B["baz"]
except KeyError:
    pass

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

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

updateset = {'foo', 'bar', 'baz'}
A.update({k: B[k] for k in updateset if k in B})

Я думаю, что общее правило здесь будет A["blah"] обычно существуют, если так попробуйте-кроме хорошо, если нет, то используйте if "blah" in b:

Я думаю, что " попробовать "дешево во времени, но" кроме " дороже.


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


лично я склоняюсь ко второму методу (но используя has_key):

if B.has_key("blah"):
  A["blah"] = B["blah"]

таким образом, каждая операция назначения-это только две строки (вместо 4 с try/except), и любые исключения, которые будут выброшены, будут реальными ошибками или вещами, которые вы пропустили (вместо того, чтобы просто пытаться получить доступ к ключам, которых нет).

как оказалось (см. комментарии к вашему вопросу), has_key устарел - поэтому я думаю, что лучше писать как

if "blah" in B:
  A["blah"] = B["blah"]

почему бы не justed сделать это:

def try_except(x,col):
    try:
        return x[col]
    except:
        return None

list(map(lambda x: try_except(x,'blah'),A))