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

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

9 голосов
/ 29 июля 2010

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

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

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

Чтобы не получить его, я бы: 1. Должен перехватить исключение или использовать get.

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

Ответы [ 5 ]

22 голосов
/ 29 июля 2010
import collections
a = collections.defaultdict(lambda: 3)
a.update({'one':1,'two':2})
print a['three']

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

Редактировать ... , если , то есть вы беспокоитесь о том, что 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__, чтобы избежать этого сложения) каждый раз платит «накладные расходы на поиск недостающего ключа».

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

7 голосов
/ 29 июля 2010

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

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

5 голосов
/ 29 июля 2010

Вот как создать подкласс 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

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

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

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

3 голосов
/ 29 июля 2010

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

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

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

0 голосов
/ 29 июля 2010

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

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

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

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

...