主题内容不是很好描述,就通过下面的例子来说明下我们经常遇到KeyError报错.
In [15]: data = {"blog":"xiaorui.cc"} In [16]: data['blog'] Out[16]: 'xiaorui.cc' In [17]: data['at'] --------------------------------------------------------------------------- KeyError Traceback (most recent call last) <ipython-input-17-9652e590cd98> in <module>() ----> 1 data['at'] KeyError: 'at' In [18]: data['at']['sign'] = 'github.com' --------------------------------------------------------------------------- KeyError Traceback (most recent call last) <ipython-input-18-8909e479937d> in <module>() ----> 1 data['at']['sign'] = 'github.com' KeyError: 'at'
文章写的不是很严谨,欢迎来喷,另外该文后续有更新的,请到原文地址查看更新。
对, 就是这个使用python字典时常常遇到的KeyError问题… 当你要实现一个比较复杂的字典,含有各种嵌套层,比如 xiaorui[‘a’][‘b’][‘c’] = “ok”, 要往c写值前要确认a b是否有,要不然会报错的. 除了写入数据之外,读取字典同样会遇到这类似的问题,get只能获取最近一层key,不能获取嵌套的字典key。
那么怎么解决? 难度我们要一层层的判断字典?
我们可以设计一个继承了dict的类,实现的方法也很是简单,只要使用__getitem__魔法函数就可以模拟友好的字典。
#blog: xiaorui.cc class MagicDict(dict): def __getitem__(self, item): try: return dict.__getitem__(self, item) except KeyError: value = self[item] = type(self)() return value a = MagicDict() a[1][2][3] = 4 a[2][2]['test'] = 6 print 'end' print a[1] for i in a: print i
python2.7的collections有个defaultdict类,他的实现跟上面类似,多加了一些废话方法而已.
#blog: xiaorui.cc In [2]: from collections import defaultdict In [3]: a = defaultdict(dict) In [4]: a['hell']['good']= 'xiaorui.cc' In [5]: a Out[5]: defaultdict(<type 'dict'>, {'hell': {'good': 'xiaorui.cc'}}) ######################################### __missing__ In [15]: d={} In [16]: d.setdefault('rui', {}).setdefault('woca', {})['nima']=3
老规矩 ! 下面我们来瞅瞅collections defaultdict的源码.
class defaultdict(dict): def __init__(self, default_factory=None, *args, **kwargs): if (default_factory is not None and not hasattr(default_factory, '__call__')): raise TypeError(b_('First argument must be callable')) dict.__init__(self, *args, **kwargs) self.default_factory = default_factory #当我们获取key时触发的函数. def __getitem__(self, key): try: return dict.__getitem__(self, key) except KeyError: return self.__missing__(key) #当我无法找到嵌套的key时会触发, 需要注意的是这里的__missing__跟method call的那个不一样. def __missing__(self, key): if self.default_factory is None: raise KeyError(key) self[key] = value = self.default_factory() return value def __reduce__(self): if self.default_factory is None: args = tuple() else: args = self.default_factory, return type(self), args, None, None, self.iteritems() def copy(self): return self.__copy__() def __copy__(self): return type(self)(self.default_factory, self) def __deepcopy__(self, memo): import copy return type(self)(self.default_factory, copy.deepcopy(self.items())) def __repr__(self): if isinstance(self.default_factory, types.MethodType) \ and self.default_factory.im_self is not None \ and issubclass(self.default_factory.im_class, defaultdict): defrepr = '<bound method sub._factory of defaultdict(...' else: defrepr = repr(self.default_factory) return 'defaultdict(%s, %s)' % (defrepr, dict.__repr__(self)) __all__ = ('defaultdict',)
对于数据格式很是混乱, 很是蛋疼的时候还是挺适合用这种友好的字典模式,如果对于数据格式的需求比较认真,那还是用普通的字典吧. 就比如为什么有很多人喜欢nosql里的mongodb,因为他的bson格式很是随意,但是这样会代码业务bug的产生… 当然还是看人,看业务…
END.