宽容的字典
我想知道怎么创建一个宽容的字典,也就是如果出现找不到键的错误,它能返回一个默认值。
在下面的代码示例中,如果我尝试访问一个不存在的键,就会出现一个键错误(KeyError);比如说:
a = {'one':1,'two':2}
print a['three']
为了避免这个错误,我需要要么捕获这个异常,要么使用get方法。
我希望我的字典不需要这样处理...
5 个回答
这里是如何根据NullUserException的建议来创建一个字典的子类
>>> 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}
如果你预期会有很多缺失的键,这一点是非常重要的
在2.5版本中,如果你有一个字典的子类,并且这个子类定义了一个叫做 __missing__() 的方法,那么当你尝试访问一个不存在的键时,比如 d[key],这个操作会自动调用 __missing__() 方法,并把你查找的键作为参数传进去。接着,d[key] 的结果就会是 __missing__(key) 方法返回的内容,或者如果这个方法抛出了错误,也会抛出那个错误。如果没有定义 __missing__() 方法,系统会抛出一个叫 KeyError 的错误。需要注意的是,__missing__() 必须是一个方法,而不能是一个实例变量。想看个例子的话,可以参考 collections.defaultdict。
import collections
a = collections.defaultdict(lambda: 3)
a.update({'one':1,'two':2})
print a['three']
这个代码会在需要的时候返回 3
。你也可以自己创建一个字典的子类,并重写 __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__
来避免添加)每次查找缺失的键时都会有“缺失键查找开销”。
你是否在意这两个问题(性能和内存占用)完全取决于你的具体使用场景。当然,了解这种权衡是个好主意!-)