4.1 dict的abc继承关系
dict是属于Mapping类型
from collections.abc import Mapping, MutableMapping
# a实际上并不是去继承了MutableMapping,其只是去实现了MutableMapping中的一些魔法函数;
a = {}
print (isinstance(a, MutableMapping))
4.2 dict的常用方法
同样,我们通过a = dict()来查看dict的源码:
a = {"bobby1":{"company":"imooc"},
"bobby2": {"company": "imooc2"}
}
#clear:清空dict
# a.clear()
# pass
#copy, 返回浅拷贝
new_dict = a.copy()
new_dict["bobby1"]["company"] = "imooc3"
print(f'new_dict:{new_dict}')
print(f'dict:{a}')
注意这里的浅拷贝,如果按照上述修改的话,new_dict是多少,a又是多少?
浅拷贝不理解的可以参考笔者之前的文章:深入理解赋值,浅拷贝,深拷贝
那么,当我们对a浅拷贝时,bobby1是拷贝过去了,bobby2也确实拷贝过去了,但是bobby1,2的value仍然是一个字典,故其value指向的引用,仍然是之前的引用,所以对浅拷贝的值更换,原来的值也依然会变化。
结果为:
new_dict:{‘bobby1’: {‘company’: ‘imooc3’}, ‘bobby2’: {‘company’: ‘imooc2’}}
dict:{‘bobby1’: {‘company’: ‘imooc3’}, ‘bobby2’: {‘company’: ‘imooc2’}}
如果是这样,则浅拷贝的值将不会变化,如下:
a = {"bobby1":"company1",
"bobby2":"company"}
new_dict = a.copy()
new_dict["bobby1"] = "imooc3"
print(f'new_dict:{new_dict}')
print(f'dict:{a}')
--------------------------------
new_dict:{'bobby1': 'imooc3', 'bobby2': 'company'}
dict:{'bobby1': 'company1', 'bobby2': 'company'}
那么如何做深拷贝呢,dict的类中没有实现,我们需要用copy的包,可以参考文章,这里不再详细叙述。
接着看fromkeys方法:
可以看到,fromkeys是一个静态方法,其说明表示它返回的是一个由可迭代对象和默认值组合成的新的字典。
#formkeys
new_list = ["bobby1", "bobby2"]
new_dict = dict.fromkeys(new_list, {"company":"imooc"})
print(f'new_dict:{new_dict}')
------------------------------------------------
new_dict:{'bobby1': {'company': 'imooc'}, 'bobby2': {'company': 'imooc'}}
get()方法:
当没有key时,会出现key-error错误,而get方法就是为了避免这种错误出现,如果没有则返回为空,或者可以自定义返回值。当然在实际编程中,通常我们自己定义的字典类不用get取值,而取json中的数据时才用get,详细可参考:Pythonic总结
value = new_dict.get("bobby1",{})
print(f'value:{value}')
items方法:
常见方法,通常用来遍历字典:
for key,value in new_dict.items():
print(key,value)
setdefault方法:
get的扩展版,当key的返回值为空时,不仅支持返回值,而且还可以对当前的空值进行填充;如果key有对应的值,则直接返回key原来对应的值,对key值不做改变。
default_value = new_dict.setdefault('bobby','imooc')
print(default_value)
update方法:
其可以将iterable对象合并,注意update无返回值;
new_dict.update({"bobby":"imooc"})
print(new_dict)
new_dict.update(bobby3="bobby3")
print(new_dict)
# 还可以支持list里放tuple的形式
new_dict.update([("bobby","imooc")])
4.3 dict的子类
不建议继承list和dict类型,因为在某些情况下,内置dict(内部是c语言书写的)其不会去调用我们覆盖的方法,如下:
#不建议继承list和dict
class Mydict(dict):
def __setitem__(self, key, value):
super().__setitem__(key, value*2)
# 没有使用我们自定义的方法来创建字典
my_dict = Mydict(one=1)
print (my_dict)
# 使用了我们自定义的方法来创建字典
my_dict["one"] = 1
print (my_dict)
--------------------------------------
{'one': 1}
{'one': 2}
那如果要去继承dict怎么办呢?可以用collection模块的子类UserDict拿来继承
from collections import UserDict
class Mydict(UserDict):
def __setitem__(self,key,value):
super().__setitem__(key,value*2)
my_dict = Mydict(one=1)
print (my_dict)
----------------------------------------------
{'one': 2}
python有一个内置的继承子类叫defaultdict,在Userdict中有一个missing方法:
当我们找不到key时,就会判断有没有missing这个魔法函数,而defaultdict其实就是重写了missing方法,如下:
missing方法当某个值找不到时,就会通过default_factory方法将某个值设置进来:
from collections import defaultdict
my_dict = defaultdict(dict)
# 当没有bobby这个key时
my_value = my_dict["bobby"]
print(my_value)
(持续更新,内部机理还会扩充。。。)
4.4 set和frozenset
set 是集合,frozenset是不可变集合,set是一个无序的集合,其不重复,在去重时用的非常多。
set的其他用法也可以参考:Pythonic总结
查看set,其接受的是一个iterable对象,即可迭代对象。所以我们用数组,字符串,列表都是可以的。
# set的结果是不重复且无序的
s = set(['a','b','c','d','e'])
print(s)
--------------------
{'d', 'e', 'c', 'a', 'b'}
# set可以添加值,而frozenset则是不可变的类型
s = {'a','b','c'}
s.add('c')
print(s)
# frozenset定义的值是不可变的,其也不可以再add添加
s = frozenset("abcde") # frozenset 可以作为dict的key
# 向set添加数据
another_set = set("def")
s.add('c')
s,update(another_set)
print(s)
# 集合的运算
another_set = set("cef")
# 差集:s - another_set: 是作为一个返回值返回,而不改变之前的s
re_set = s.difference(another_set)
re_set = s - another_set
# 交集
re_set = s & another_set
# 并集
re_set = s | another_set
print(re_set)
# set性能很高
# 判断某个元素是否在集合当中
if "c" in re_set:
print ("i am in set")
# 因为其实现了__contains__的魔法函数,所以其可以做in的判断;
# 判断集合是不是另一集合的一部分
print (s.issubset(re_set))
4.5 dict和set的实现原理
我们通过同一个文件中找寻字符串的速率来比较dict,list和set的性能(过程略):
dict查找的性能要远远大于list,在list中随着list数据的增大,查找时间会增大,在dict中查找元素不会随着sict的增大而增大。
dict背后的实现原理其实是散列表(hash表),有一段连续的数组,数组中存储了指向key和value的指针。我们要存一个值到我们的哈希中,其会首先由hash()函数计算我们的数值,这个数值可以理解为一个偏移量,即将key和value放在偏移值的位置上。如果有冲突的情况,则采用某种规则进行顺移。
数组相比链表最大的优点就是可以根据偏移量来直接获取值,做到任何一个位置直接获取。
字典dict中的哈希表查找过程: