Прощающий словарь

Я задаюсь вопросом, как создать прощающий словарь (тот, который возвращает значение по умолчанию, если KeyError повышен).

В следующем примере кода я получил бы KeyError; например,

a = {'one':1,'two':2}
print a['three']

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

Я хотел бы, чтобы не должным быть сделать это с моим словарем...

9
задан Raymond Hettinger 23 February 2017 в 03:33
поделиться

5 ответов

import collections
a = collections.defaultdict(lambda: 3)
a.update({'one':1,'two':2})
print a['three']

при необходимости излучает 3 . Вы также можете создать подкласс dict и переопределить __ missing __ , но это не имеет особого смысла, когда поведение defaultdict (игнорируя точный отсутствующий ключ, который ищется ) так вам идет ...

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

>>> import sys
>>> a = collections.defaultdict(lambda: 'blah')
>>> print len(a), sys.getsizeof(a)
0 140
>>> for i in xrange(99): _ = a[i]
... 
>>> print len(a), sys.getsizeof(a)
99 6284

... defaultdict, изначально пустой, теперь содержит 99 ранее отсутствующих ключей, которые мы искали, и занимает 6284 байта (по сравнению с 140 байтами, которые потребовались, когда он был пуст).

Альтернативный подход ...:

>>> class mydict(dict):
...   def __missing__(self, key): return 3
... 
>>> a = mydict()
>>> print len(a), sys.getsizeof(a)
0 140
>>> for i in xrange(99): _ = a[i]
... 
>>> print len(a), sys.getsizeof(a)
0 140

... как видите, полностью избавляет от лишних расходов на память. Конечно, производительность - еще одна проблема:

$ python -mtimeit -s'import collections; a=collections.defaultdict(int); r=xrange(99)' 'for i in r: _=a[i]'
100000 loops, best of 3: 14.9 usec per loop

$ python -mtimeit -s'class mydict(dict):
>   def __missing__(self, key): return 0
> ' -s'a=mydict(); r=xrange(99)' 'for i in r: _=a[i]'
10000 loops, best of 3: 92.9 usec per loop

Поскольку defaultdict добавляет (ранее отсутствующий) ключ при поиске, он становится намного быстрее при следующем поиске такого ключа, в то время как mydict (который переопределяет __ missing __ , чтобы избежать этого добавления) каждый раз оплачивает «накладные расходы на поиск отсутствующего ключа».

Независимо от того, волнует ли вас какая-либо проблема (производительность или объем памяти), конечно, полностью зависит от вашего конкретного варианта использования. Это в любом случае хорошая идея знать о компромиссе! -)

22
ответ дан 4 December 2019 в 06:29
поделиться

Вы, вероятно, захотите использовать defaultdict (я полагаю, для этого требуется как минимум python2.5)

from collections import defaultdict
def default(): return 'Default Value'
d = defaultdict(default)
print(d['?'])

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

3
ответ дан 4 December 2019 в 06:29
поделиться

Новое в версии 2.5: Если подкласс класса dict определяет метод __missing__(), если ключ key отсутствует, то операция операция d[key] вызывает этот метод с ключевым ключом в качестве аргумента. Операция операция d[key] затем возвращает или возвращает или поднимает то, что было возвращено или поднято вызовом __missing__(key), если ключ не присутствует. Никакие другие операции или методы не вызывают __missing__(). Если __missing__() не определена, будет выдана ошибка KeyError. __missing__() должна быть методом; она не может быть переменной экземпляра. Для примера пример, см. collections.defaultdict.

http://docs.python.org/library/stdtypes.html

7
ответ дан 4 December 2019 в 06:29
поделиться

Вот как создать подкласс dict , как было предложено NullUserException

>>> class forgiving_dict(dict):
...     def __missing__(self, key):
...         return 3
...
>>> a = forgiving_dict()
>>> a.update({'one':1,'two':2})
>>> print a['three']
3

. Одно большое различие между этим ответом и ответом Алекса состоит в том, что отсутствующий ключ - not добавлен в словарь

>>> print a
{'two': 2, 'one': 1}

Что очень важно, если вы ожидаете много промахов

5
ответ дан 4 December 2019 в 06:29
поделиться

Иногда то, что вам действительно нужно, это .setdefault () , что не очень интуитивно понятно, но это метод, который «возвращает указанный ключ, если он не существует, установите этот ключ на это значение ".

Вот пример использования setdefault () для хорошего результата:

collection = {}
for elem in mylist:
    key = key_from_elem(elem)
    collection.setdefault(key, []).append(elem)

Это позволит нам создать такой словарь: {'key1': [elem1, elem3], 'key2': [elem3]} без уродливой проверки наличия ключа и создания для него списка.

0
ответ дан 4 December 2019 в 06:29
поделиться
Другие вопросы по тегам:

Похожие вопросы: