标准库里的全部映射类型都是利用 dict 来实现的
只有可散列的数据类型才能用做这些映射里的键(值不用)python
DIAL_CODES = [(86, 'China'), (91, 'India'), (1, 'United States'), (62, 'Indonesia') ] country_code = {country: code for code, country in DIAL_CODES }
结果
{'China': 86, 'India': 91, 'United States': 1, 'Indonesia': 62}
##找对应的key,没有的话返回默认值 my_dict = {"name":"longe","age":8} my_dict.setdefault("namerrr","default") print(my_dict)
用 setdefault 只须要一次就能够完成整个操做。测试
原理
全部这一切背后的功臣实际上是特殊方法 __missing__。
它会在defaultdict 遇到找不到的键的时候调用 default_factory
自定义一个映射类型,更合适的策略实际上是继承collections.UserDict 类spa
只是为了演示 missing 是如何被dict.__getitem__ 调用的。code
class StrKeyDict0(dict): def __missing__(self, key): if isinstance(key, str): raise KeyError(key) return self[str(key)] def get(self, key, default=None): try: return self[key] except KeyError: return default def __contains__(self, key): return key in self.keys() or str(key) in self.keys()
isinstance(key, str) 测试在上面的__missing__ 中是必需的
可是若是 str(k) 不是一个存在的键,代码就会陷入无限递归。
这是由于 missing 的最后一行中的 self[str(key)] 会调用 __getitem__,
而这个 str(key) 又不存在,因而 __missing__又会被调用。对象
精简版本blog
import collections class StrKeyDict(collections.UserDict): def __missing__(self, key): if isinstance(key, str): raise KeyError(key) return self[str(key)] def __contains__(self, key): return str(key) in self.data def __setitem__(self, key, item): self.data[str(key)] = item
setitem 会把全部的键都转换成字符串。因为把具体的实现委
托给了 self.data 属性,这个方法写起来也不难
这个类型在添加键的时候会保持顺序,所以键的迭代次序老是一致
的。继承
该类型能够容纳数个不一样的映射对象,而后在进行键查找操做的时
候,这些对象会被看成一个总体被逐个查找,直到键被找到为止。递归
这个映射类型会给键准备一个整数计数器。每次更新一个键的时候
都会增长这个计数器。ip
这个类其实就是把标准 dict 用纯 Python 又实现了一遍。
跟 OrderedDict、ChainMap 和 Counter 这些开箱即用的类型不
同,UserDict 是让用户继承写子类的。下面就来试试。内存
from unicodedata import name aa = {chr(i) for i in range(32, 256) if 'SIGN' in name(chr(i), '')} print(aa)
到这个阈值的时候,原有的散列表会被复制到一个更大的空间里面。
添加新元素和更新现有键值的操做几乎跟上面同样。
只不过对于前者,在发现空表元的时候会放入一个新元素;
对于后者,在找到相对应的表元后,原表里的值对象会被替换成新值。
字典浪费存储空间(不过没有几百万对象,内存好几个G不用考虑)
dict 的实现是典型的空间换时间:字典类型有着巨大的内存开销
当往 dict 里添加新键而又发生散列冲突的时候,新键可能会被安
排存放到另外一个位置。
容的决定。
能会跳过一些键——甚至是跳过那些字典中已经有的键。
update。
表),从而避免了重复的键搜索。