Почему dict.get(ключ) вместо dict[ключ]?

Сегодня я наткнулся на dict метод get который, учитывая ключ в словаре, возвращает соответствующее значение.

Для чего эта функция полезна? Если я хочу найти значение, связанное с ключом в словаре, я могу просто сделать dict[key]и возвращает то же самое:

dictionary = {"Name": "Harry", "Age": 17}
dictionary["Name"]
dictionary.get("Name")

19 ответов

Решение

Это позволяет вам указать значение по умолчанию, если ключ отсутствует:

dictionary.get("bogus", default_value)

возвращается default_value (что бы вы ни выбрали), тогда как

dictionary["bogus"]

поднимет KeyError,

Если опущено, default_value является Noneтакой, что

dictionary.get("bogus")  # <-- No default specified -- defaults to None

возвращается None как

dictionary.get("bogus", None)

было бы.

Что dict.get()метод?

Как уже упоминалосьgetМетод содержит дополнительный параметр, который указывает на отсутствующее значение. Из документации

get(key[, default])

Возвращает значение для ключа, если ключ находится в словаре, иначе по умолчанию. Если default не задано, по умолчанию используется None, поэтому этот метод никогда не вызываетKeyError,

Примером может быть

>>> d = {1:2,2:3}
>>> d[1]
2
>>> d.get(1)
2
>>> d.get(3)
>>> repr(d.get(3))
'None'
>>> d.get(3,1)
1

Есть ли улучшения скорости где-нибудь?

Как уже упоминалось здесь,

Кажется, что все три подхода в настоящее время демонстрируют схожую производительность (в пределах примерно 10% друг от друга), более или менее независимую от свойств списка слов.

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

def getway(d):
    for i in range(100):
        s = d.get(i)

def lookup(d):
    for i in range(100):
        s = d[i]

Теперь синхронизируем эти две функции, используяtimeit

>>> import timeit
>>> print(timeit.timeit("getway({i:i for i in range(100)})","from __main__ import getway"))
20.2124660015
>>> print(timeit.timeit("lookup({i:i for i in range(100)})","from __main__ import lookup"))
16.16223979

Как мы видим, поиск выполняется быстрее, чем поиск, так как поиск функции отсутствует. Это можно увидеть через dis

>>> def lookup(d,val):
...     return d[val]
... 
>>> def getway(d,val):
...     return d.get(val)
... 
>>> dis.dis(getway)
  2           0 LOAD_FAST                0 (d)
              3 LOAD_ATTR                0 (get)
              6 LOAD_FAST                1 (val)
              9 CALL_FUNCTION            1
             12 RETURN_VALUE        
>>> dis.dis(lookup)
  2           0 LOAD_FAST                0 (d)
              3 LOAD_FAST                1 (val)
              6 BINARY_SUBSCR       
              7 RETURN_VALUE  

Где это будет полезно?

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

 if key in dic:
      val = key[dic]
 else:
      val = def_val

Для одной строки,val = dic.get(key,def_val)

Где это будет НЕ полезно?

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

Возможно ли иметьgetкак особенность вdict['key']?

Да! Нам нужно реализовать__missing__ в подклассе dict.

Пример программы может быть

class MyDict(dict):
    def __missing__(self, key):
        return None

Небольшая демонстрация может быть

>>> my_d = MyDict({1:2,2:3})
>>> my_d[1]
2
>>> my_d[3]
>>> repr(my_d[3])
'None'

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

dictionary = {"Name": "Harry", "Age": 17}
dictionary.get('Year', 'No available data')
>> 'No available data'

Если вы не дадите второй параметр, None будет возвращен.

Если вы используете индексирование как в dictionary['Year']несуществующие ключи подымут KeyError,

О чем нужно помнить при использовании .get():

Если словарь содержит ключ, использованный при вызове .get() и его ценность None, то .get() метод вернет None даже если указано значение по умолчанию.

Например, следующие результаты: Noneне 'alt_value' как и следовало ожидать:

d = {'key': None}
d.get('key', 'alt_value')

.get()второе значение возвращается только в том случае, если предоставленный ключ НЕ находится в словаре, а не если возвращаемое значение этого вызова None.

Я приведу практический пример очистки веб-данных с помощью Python, во многих случаях вы получите ключи без значений, в этих случаях вы получите ошибки, если будете использовать словарь ['key'], тогда как dictionary.get('ключ ', 'return_otherwise') не имеет проблем.

Точно так же, я бы использовал ''.join(list), а не list[0], если вы попытаетесь захватить одно значение из списка.

Надеюсь, поможет.

[Править] Вот практический пример:

Скажем, вы вызываете API, который возвращает файл JOSN, который вам нужно проанализировать. Первый JSON выглядит следующим образом:

{"bids":{"id":16210506,"submitdate":"2011-10-16 15:53:25","submitdate_f":"10\/16\/2011 at 21:53 CEST","submitdate_f2":"p\u0159ed 2 lety","submitdate_ts":1318794805,"users_id":"2674360","project_id":"1250499"}}

Второй JOSN выглядит так:

{"bids":{"id":16210506,"submitdate":"2011-10-16 15:53:25","submitdate_f":"10\/16\/2011 at 21:53 CEST","submitdate_f2":"p\u0159ed 2 lety","users_id":"2674360","project_id":"1250499"}}

Обратите внимание, что во втором JSON отсутствует ключ "submitdate_ts", что вполне нормально для любой структуры данных.

Поэтому, когда вы пытаетесь получить доступ к значению этого ключа в цикле, вы можете вызвать его с помощью следующего:

for item in API_call:
    submitdate_ts = item["bids"]["submitdate_ts"]

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

Подходящим способом кодирования этого может быть следующее:

for item in API_call:
    submitdate_ts = item.get("bids", {'x': None}).get("submitdate_ts")

{'x': Нет}, чтобы избежать получения ошибки на втором уровне. Конечно, вы можете повысить отказоустойчивость в коде, если выполняете очистку. Как сначала указать условие if

Цель состоит в том, чтобы вы могли дать значение по умолчанию, если ключ не найден, что очень полезно

dictionary.get("Name",'harry')

Для чего эта функция полезна?

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

fruits = ['apple', 'banana', 'peach', 'apple', 'pear']
d = {}
for fruit in fruits:
    if fruit not in d:
        d[fruit] = 0
    d[fruit] += 1

Используя метод.get(), вы можете сделать этот код более компактным и понятным:

for fruit in fruits:
      d[fruit] = d.get(fruit, 0) + 1

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

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

Автор сообщения в блоге утверждает, что «защищает ваш код»:

Обратите внимание, как попытка сослаться на несуществующий термин приводит к KeyError. Это может вызвать серьезные проблемы, особенно при работе с непредсказуемыми бизнес-данными.

Хотя мы могли бы обернуть наше выражение в / или ifутверждение, такое большое внимание к словарному термину быстро накапливается.

Это правда, что в редком случае объединения null () или иного заполнения отсутствующего значения для обработки непредсказуемых динамических данных разумно развернутый является полезным и сокращенным инструментом Python для неуклюжих if key in dct:а также / exceptблоки, которые существуют только для установки значений по умолчанию, когда ключ может отсутствовать как часть поведенческой спецификации для программы.

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

Распространенная ошибка среди программистов состоит в том, что они думают, что исключения вызывают головную боль, и пытаются их подавить, используя такие методы, как упаковка кода в ... except: passблоки . Позже они понимают, что настоящая головная боль заключается в том, чтобы никогда не увидеть нарушение логики приложения в момент сбоя и развертывание неработающего приложения. Лучшей практикой программирования является использование утверждений для всех инвариантов программы, таких как ключи, которые должны быть в словаре.

Иерархия безопасности ошибок в широком смысле такова:

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

Python, задуманный как интерпретируемый язык, в значительной степени зависит от исключений во время выполнения, а не от ошибок компилятора. Отсутствующие методы или свойства, недопустимые операции с типами, такие как 1 + "a"и выход за пределы или отсутствующие индексы или ключи поднимаются по умолчанию.

Некоторые языки, такие как JS, Java, Rust и Go, используют резервное поведение для своих карт по умолчанию (и во многих случаях не предоставляют альтернативу throw/raise), но Python создает исключения по умолчанию, наряду с другими языками, такими как C#. Perl/PHP выдает предупреждение о неинициализированном значении.

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

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

  • Предпочитаете писать стандартный, идиоматический код, используя инструменты, предоставляемые языком. Программисты Python обычно (правильно) предпочитают квадратные скобки из соображений безопасности исключений, указанных выше, и потому, что это поведение по умолчанию для дикторов Python.
  • Всегда используйте намерение неустойки, создавая случаи, когда вы ожидаете предоставить значение по умолчанию, неотличимое от поиска, который, как вы утверждаете, должен быть успешным.
  • Тестирование усложняется пропорционально новым «легальным» путям программы, разрешенным . По сути, каждый поиск теперь представляет собой ветвь, которая может быть успешной или неудачной — оба случая должны быть проверены, чтобы установить покрытие, даже если путь по умолчанию фактически недоступен по спецификации (по иронии судьбы это приводит к дополнительным if val is not None:или же tryдля всех будущих применений полученного значения; ненужным и запутанным для чего-то, что никогда не должно быть Noneв первую очередь).
  • немного медленнее .
  • труднее набирать и уродливее читать (сравните ArrayListсинтаксис для нативного C# Listsили векторный код C++). Незначительный.

Некоторые языки, такие как C++ и Ruby, предлагают альтернативные методы ( atа также fetch, соответственно), чтобы согласиться на выдачу ошибки при неправильном доступе, в то время как C# предлагает резервное значение согласия. TryGetValueпохоже на Python get.

Поскольку JS, Java, Ruby, Go и Rust по умолчанию внедряют резервный подход во все поиски хэшей, можно подумать, что это не так уж плохо. Это правда, что это не самая большая проблема, стоящая перед разработчиками языков, и существует множество вариантов использования версии без броска доступа, поэтому неудивительно, что между языками нет единого мнения.

Но, как я уже говорил, Python (наряду с C#) добился большего успеха, чем эти языки, сделав параметр assert параметром по умолчанию. Это потеря безопасности и выразительности, чтобы отказаться от использования его для сообщения о нарушениях контракта в момент сбоя путем неизбирательного использования .getпересечь границу.

Почему dict.get(ключ) вместо dict[ключ]?

0. Резюме

По сравнению с dict[key], dict.get обеспечивает запасное значение при поиске ключа.

1. Определение

get (key [, default]) 4. Встроенные типы - документация по Python 3.6.4rc1

Возвращает значение для ключа, если ключ находится в словаре, иначе по умолчанию. Если default не задано, по умолчанию None, поэтому этот метод никогда не вызывает KeyError.

d = {"Name": "Harry", "Age": 17}
In [4]: d['gender']
KeyError: 'gender'
In [5]: d.get('gender', 'Not specified, please add it')
Out[5]: 'Not specified, please add it'

2. Проблема, которую он решает.

Если без default valueВы должны написать громоздкие коды для обработки такого исключения.

def get_harry_info(key):
    try:
        return "{}".format(d[key])
    except KeyError:
        return 'Not specified, please add it'
In [9]: get_harry_info('Name')
Out[9]: 'Harry'
In [10]: get_harry_info('Gender')
Out[10]: 'Not specified, please add it'

Как удобное решение, dict.get вводит необязательное значение по умолчанию, позволяющее избежать вышеуказанных кодов.

3. Вывод

dict.get имеет дополнительную опцию значения по умолчанию для обработки исключения, если ключ отсутствует в словаре

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

print(dictionary.get("address")) # None
print(dictionary["address"]) # throws KeyError: 'address'

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

Поэтому вместо того, чтобы делать это (или что-то подобное):

score = None
try:
    score = dictionary["score"]
except KeyError:
    score = 0

Мы можем это сделать:

score = dictionary.get("score", 0)
# score = 0

Еще один вариант использования, о котором я не упоминаю, - это key аргумент для таких функций, как sorted, max а также min. В get Метод позволяет возвращать ключи на основе их значений.

      >>> ages = {"Harry": 17, "Lucy": 16, "Charlie": 18}
>>> print(sorted(ages, key=ages.get))
['Lucy', 'Harry', 'Charlie']
>>> print(max(ages, key=ages.get))
Charlie
>>> print(min(ages, key=ages.get))
Lucy

Благодаря этому ответу на другой вопрос за предоставление этого варианта использования!

Короткий ответ

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

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

Базовый метод и вспомогательный метод

Квадратные скобки называют__getitem__метод, который является фундаментальным для отображений, таких как dicts.

The get()метод является вспомогательным слоем поверх этой функциональности. Это сокращение для общего шаблона кодирования:

      try:
    v = d[k]
except KeyError:
    v = default_value  

Для каких целей полезна эта функция?

Еще один вариант использования, гдеget()полезно, если оно вызывает встроенную функцию из словаря. Как уже упоминалось в других ответах, значение по умолчанию может быть указано дляdict.get, что означает, что сам ключ может быть возвращен, если его нет в словаре, напримерmy_dict.get(key, key). Это означает, что мы можем очень кратко использовать замену значений.

Например, из словаряdct = {1: 10}, мы можем создать функциюreplacer = dct.get(type(mapper)возвращаетbuiltin_function_or_method). Затем эту функцию можно сопоставить для замены значений.

      lst = [0, 1, 2, 3, 4]
new_list = list(map(replacer, lst, lst))   # [0, 10, 2, 3, 4]

На самом деле поиск значений с помощью функции, вызываемойdict.get(). Следующий эксперимент показывает, что поиск с помощью функции происходит более чем в 2 раза быстрее, чем поиск с помощью словаря (это было сделано на Python 3.9.12).

      import timeit
setup = "lst = [0,1]*10000; dct = {1: 10}; replacer = dct.get"
t1 = min(timeit.repeat("list(map(replacer, lst, lst))", setup, number=100))
t2 = min(timeit.repeat("[dct[k] if k in dct else k for k in lst]", setup, number=100))

print(t2 / t1)   # 2.707056842200316

Кроме того, функцияdiction.get позволяет указать значение по умолчанию, если ключ не существует, а другой нет. Вы можете вернуть None, если ключ не существует. Синтаксис:

      dictionary.get('key', default)

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

       instance.first_name = validated_data.get('first_name',instance.first_name) 
 instance.last_name = validated_data.get('last_name',instance.first_name)
 instance.save()
 
  • dict.get по умолчанию ничего не вернет, если ключ не существует, но если вы укажете второй аргумент, он вернет его, если ключ не существует.

  • Ото dict[key] поднимет KeyError если ключ не существует

Вот пример (читай комментарии):

>>> d={'a':[1,2,3],'b':[4,5,6]} # Create a dictionary
>>> d['c'] # Hoops, error key does not exist
Traceback (most recent call last):
  File "<pyshell#7>", line 1, in <module>
    d['c']
KeyError: 'c'
>>> d.get('c') # no error because of `get`, so nothing returned
>>> print(d.get('c')) # i print it, oh `None` is the output
None
>>> d.get('c',100) # Okay now i set second argument's value to `100`, hoopa output is `100`
100
>>> d['a'] # Works, key exist
[1, 2, 3]
>>> d.get('a') # work too, key exist
[1, 2, 3]

В Python 3.8 и более поздних версиях метод словаря можно использовать с оператором walrus.:=в выражении присваивания для дальнейшего сокращения кода:

      if (name := dictonary.get("Name")) is not None
    return name

С использованием[]вместоget()потребуется обернуть код в блок try/except и перехватитьKeyError(не показано). А без оператора walrus вам понадобилась бы еще одна строка кода:

      name = dictionary.get("Name")
if (name is not None)
    return name

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

      class dictionary():
    def get(self,key,default):
         if self[key] is not found : 
               return default
         else:
               return self[key]

В зависимости от использования следует использовать это get метод.

Example1

In [14]: user_dict = {'type': False}

In [15]: user_dict.get('type', '')

Out[15]: False

In [16]: user_dict.get('type') or ''

Out[16]: ''

Example2

In [17]: user_dict = {'type': "lead"}

In [18]: user_dict.get('type') or ''

Out[18]: 'lead'

In [19]: user_dict.get('type', '')

Out[19]: 'lead'

.get()дает вам «неявное»try: ... except:, что делает код чище и надежнее, когда вы к нему привыкнете.

Другие вопросы по тегам