9

我想知道如何创建宽容的字典(如果引发 KeyError 则返回默认值的字典)。

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

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

为了不得到一个,我会 1. 必须捕获异常或使用 get。

我不想用我的字典这样做......

4

5 回答 5

22
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__以避免添加)每次都支付“丢失的键查找开销”。

当然,您是否关心任何一个问题(性能与内存占用)完全取决于您的特定用例。无论如何,了解权衡是个好主意!- )

于 2010-07-29T00:22:05.303 回答
7

2.5 版新功能:如果 dict 的子类定义了方法 __missing__(),如果键 key 不存在,则 d[key] 操作以键 key 作为参数调用该方法。如果键不存在,则 d[key] 操作将返回或引发 __missing__(key) 调用返回或引发的任何内容。没有其他操作或方法调用 __missing__()。如果 __missing__() 未定义,则会引发 KeyError。__missing__() 必须是一个方法;它不能是实例变量。例如,请参阅 collections.defaultdict。

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

于 2010-07-29T00:22:07.737 回答
6

以下是如何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}

如果您预计会有很多失误,这非常重要

于 2010-07-29T01:12:20.917 回答
3

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

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

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

于 2010-07-29T00:25:42.567 回答
0

有时您真正想要的.setdefault()是不是很直观,但它是一种“返回指定的键,如果它不存在,则将该键设置为该值”的方法。

这是一个setdefault()使用效果很好的例子:

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

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

于 2010-07-29T03:05:04.590 回答