Python 宽容的字典

Python 宽容的字典,python,dictionary,defaultdict,dictionary-missing,Python,Dictionary,Defaultdict,Dictionary Missing,我想知道如何创建宽恕字典(如果出现KeyError,它将返回默认值) 在下面的代码示例中,我会得到一个KeyError;比如说 a = {'one':1,'two':2} print a['three'] 为了不得到一个,我会1。必须捕获异常或使用get 我不想用我的字典做那件事 import collections a = collections.defaultdict(lambda: 3) a.update({'one':1,'two':2}) print a['three'] 根据需要

我想知道如何创建宽恕字典(如果出现KeyError,它将返回默认值)

在下面的代码示例中,我会得到一个KeyError;比如说

a = {'one':1,'two':2}
print a['three']
为了不得到一个,我会1。必须捕获异常或使用get

我不想用我的字典做那件事

import collections
a = collections.defaultdict(lambda: 3)
a.update({'one':1,'two':2})
print a['three']
根据需要发出
3
。您也可以自己对
dict
进行子类化,并覆盖
\uuuuuuu missing\uuuuuuuu
,但是当
defaultdict
行为(忽略正在查找的确切缺失键)非常适合您时,这就没有多大意义了

编辑…除非,也就是说,您担心
一个
在每次查找缺少的键(这是
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
(它覆盖
\uuuuuuu缺少的
以避免添加)每次都会支付“缺少的键查找开销”

当然,您是否关心这两个问题(性能与内存占用)完全取决于您的特定用例。在任何情况下,注意权衡都是一个好主意!-)

版本2.5中新增:如果 dict定义了一个方法uuu缺少uuu(), 如果钥匙不存在,则 d[key]操作调用该方法 以键作为参数。这个 d[键]操作然后返回或 引发返回或引发的任何内容 如果 钥匙不存在。没有别的 操作或方法调用 __缺少\ \()。如果未定义uuu missing_uuuu(),则会引发KeyError。 __缺少的\必须是一个方法;它不能是实例变量。为了 示例,请参见collections.defaultdict


您可能需要使用defaultdict(我相信它至少需要python2.5)


传递给构造函数的函数告诉类返回什么作为默认值。有关其他示例,请参见。

以下是如何按照NullUserException的建议对
dict
进行子类化

>>> class forgiving_dict(dict):
...     def __missing__(self, key):
...         return 3
...
>>> a = forgiving_dict()
>>> a.update({'one':1,'two':2})
>>> print a['three']
3
这个答案与Alex的答案之间的一大区别是缺少的键不是添加到字典中的

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

如果您期望大量未命中,这一点非常重要。有时您真正想要的是
.setdefault()
,这不是很直观,但它是一种“返回指定的键,如果它不存在,请将该键设置为此值”的方法

下面是一个使用
setdefault()
效果良好的示例:

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

这将允许我们创建这样一个字典:
{'key1':[elem1,elem3],'key2':[elem3]}
无需进行丑陋的检查以查看是否已经存在密钥并为其创建列表。

集合。defaultdict
是您的电池内置解决方案。警告:defaultdict在返回给定密钥的默认值时会将新项插入自身。这就把读操作变成了潜在的写操作,这意味着查找大量丢失的键将使其快速增长。棒极了!从第二段到最后一段似乎与您的示例不相关,因为您从未使用同一个键两次。因此,即使您从不重复某个键,defaultdict似乎也会更快,如果您重复了,则会更快。是这样吗?@Jeff,
timeit
循环您正在测量的语句,因此它重复该语句——在本例中,
for i in r
循环。+1我从未意识到这两种方法的性能/内存权衡。谢谢你启发我。
collection = {}
for elem in mylist:
    key = key_from_elem(elem)
    collection.setdefault(key, []).append(elem)