深入set和dict
dict中的抽象关系,collections中的abc,Mapping和MutableMapping(继承Mapping)
MutableMapping的方法:__setitem__\__delitem__\pop\popitem\clear\update\setdefault,
dict是属于Mapping、MutableMapping类型,而Mapping和序列差不多,都是继承collections,
但Mapping也另外实现了序列没有的其他方法:例如get\keys\items\values
from collections.abc import Mapping,MutableMapping
a_dict = {}
# a_dict并不是继承MutableMapping,而只是实现了MutableMapping的一些方法,在MutableMapping.register(dict)
print(isinstance(a_dict,MutableMapping))
# 结果是True,但不是继承关系
print(dict.mro())
# 字典的继承关系是 [<class 'dict'>, <class 'object'>],并没有mutableMapping
dict常用方法:
clear\copy\fromkeys\get\items\keys\pop\popitem\setdefault\update\values\
a = {"bobby1":{"company":"imooc"},
"bobby2":{"company":"imooc2"}}
# copy返回浅拷贝,只拷贝了键,而值是公用的,修改后原字典也会受影响,牵一发动全身
new_dict = a.copy()
# dict实现了copy浅拷贝方法,却没有实现deepcopy深拷贝,所以使用深拷贝需要用copy.deepcopy
new_dict["bobby1"]["company"] = "imooc3"
# deepcopy深拷贝,键-值共同拷贝,修改不受影响
new_dict = copy.deepcopy(a)
new_dict["bobby1"]["company"] = "imooc4"
# fromkeys:将iterable对象(如序列)作为键值
# 是dict中的静态方法staticmethod,需要用类名调用即dict.staticmethod
new_list = ["bobby1","bobby2"]
# new_list作为iterable对象,变为键值
new_dict = dict.fromkeys(new_list,{"company":"imooc"})
当取值无key,用get可避免报错,但无法插入字典。
当字典无key,用setdefault可避免报错,同时插入字典中(先调用get,再设置字典key键和值)
#get:当字典直接通过键去获取值时,经常出现keyerror错误。为了防止这个错误,可使用get方式,设置报错时的默认值,但最后没有插入字典
value = new_dict.get("bobby1",{})
default方法和update方法
default方法:会调用get(key,d值),如果字典中无key,则返回d值,同时将D[key]=d值(默认值)插入到字典中,最后返回d值
如果字典中有key,则得到并返回D[key]值,
default_value = new_dict.setdefault("bobby","imooc")
new_dict = {"1":"name",
"2":"sex"}
value = new_dict.get("3","class")
print(value)
print(new_dict) # 无“3”:"calss"
value = new_dict.setdefault("3","class")
print(value)
print(new_dict) # 有“3”:"class"
# update方法:合并dict或iterable对象
new_dict.update({"4":"home"})
new_dict.update(5="student") # 报错,因为key是变量才能这么用赋值号,5不能作为变量,
new_dict.update(bobby="imooc")
new_dict.update([("bobby1","imooc")])
dict中的子类,dict和list是可继承的,但不建议继承,dict\list都是C语言封装好的
class Mydict(dict):
def __setitem__(self,key,value):
super().__setitem__(key,value*2)
my_dict = Mydict(one=1) # 实例化对象时,调用的是__init__方法
print(my_dict) # 出现{"one":1},在实例化对象时,不会去调用__setitem__方法
my_dict["one"] = 1
print(my_dict) # 出现{"one":2},在设置值时,才会调用__setitem__方法
在collection中,有用Python写的UserDict,这个可以继承
class Mydict(UserDict):
def __setitem__(self,key,value):
super().__setitem__(key,value*2)
my_dict = Mydict(one=1) # 实例化对象时,调用的是__init__方法,但同时也会调用__setitem__方法
print(my_dict) # 出现{"one":2},在实例化对象时,会调用__setitem__方法
my_dict["one"] = 1
print(my_dict) # 出现{"one":2},在设置值时,会调用__setitem__方法
在collections中,有个defaultdict是dict的子类
from collections import defaultdict
my_dict = defaultdict(dict)
my_value = my_dict["bobby"] # 当字典中没有key时,就会进入__missing__方法,返回空字典
set和frozenset(集合和不可变集合)无序,不重复
s = set("abcde") # 接收可迭代对象
s = set(['a','b','c','d','e'])
print(s) # 顺序是无序的
# 通过add添加元素
s = {'a','b'} # 用{}也是set
# 但frozenset是不可变类型,因此不能使用add,由于frozenset不可变,所以可以作为dict的key
s = frozenset("abcde")
s.add("f") # 无效的add,因frozenset不可变
update:将两个set合并为一个set
s = set("abcde")
another_set = set("def")
s.update(another_set) # s发生改变
print(s)
difference:集合中不存在于其他集合中的数据
s = set("abcde")
ano_set = set("defg")
re_set = s.difference(ano_set) # 等同于s-ano_set
print(s) # s没发生改变
print(re_set) # re_set结果是abc
集合中的常见运算:union|并集 &交集 -差集,set和dict使用hash
,性能很高
魔法函数__ior__ return self/=value
__isub__ return self-=value
__ixon__ return self^=value
re_set = s-ano_set # 差集
re_set = s&ano_set # 交集
re_set = s|ano_set # 并集
s.is_subset(re_set) :判断s是否为re_set的子集
-
dict和set的实现原理:使用哈希表:将键值(或set里的值)计算成哈希值,作为地址索引,产生哈希冲突可解决
-
哈希表会申请连续的地址空间,如果空间不够用就会再申请更大的空间,然后把数据复制过去。
-
查找过程:计算散列值——判断散列值的一部分来定位表元,表元为空(抛出keyerror),表元不为空——健是否相等(有哈希冲突)——相等则返回,不相等则使用散列值的另一部分定位散列表中的另一行
1、不可变对象都可以计算hash值。例如str\frozenset\tuple
。
2、自己实现的类,可实现__hash__
,对象即可哈希
3、dict内存花销大,但是查询速度快。自己定义的对象,或是python内部的对象,都是用dict包装的
4、添加数据有可能改变已有数据的顺序(插入数据可能会重新申请更大的内存,数据顺序有可能改变)