05 python深入python的set和dict
dict的abc继承关系
dict属于mapping类型,下面通过查看源码来分析它的继承关系
from collections.abc import Mapping, MutableMapping
查看MutableMapping源码
class MutableMapping(Mapping): __slots__ = () """A MutableMapping is a generic container for associating key/value pairs. This class provides concrete generic implementations of all methods except for __getitem__, __setitem__, __delitem__, __iter__, and __len__. """ @abstractmethod def __setitem__(self, key, value): raise KeyError @abstractmethod def __delitem__(self, key): raise KeyError __marker = object() def pop(self, key, default=__marker): '''D.pop(k[,d]) -> v, remove specified key and return the corresponding value. If key is not found, d is returned if given, otherwise KeyError is raised. ''' try: value = self[key] except KeyError: if default is self.__marker: raise return default else: del self[key] return value def popitem(self): '''D.popitem() -> (k, v), remove and return some (key, value) pair as a 2-tuple; but raise KeyError if D is empty. ''' try: key = next(iter(self)) except StopIteration: raise KeyError value = self[key] del self[key] return key, value def clear(self): 'D.clear() -> None. Remove all items from D.' try: while True: self.popitem() except KeyError: pass def update(*args, **kwds): ''' D.update([E, ]**F) -> None. Update D from mapping/iterable E and F. If E present and has a .keys() method, does: for k in E: D[k] = E[k] If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v In either case, this is followed by: for k, v in F.items(): D[k] = v ''' if not args: raise TypeError("descriptor 'update' of 'MutableMapping' object " "needs an argument") self, *args = args if len(args) > 1: raise TypeError('update expected at most 1 arguments, got %d' % len(args)) if args: other = args[0] if isinstance(other, Mapping): for key in other: self[key] = other[key] elif hasattr(other, "keys"): for key in other.keys(): self[key] = other[key] else: for key, value in other: self[key] = value for key, value in kwds.items(): self[key] = value def setdefault(self, key, default=None): 'D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D' try: return self[key] except KeyError: self[key] = default return default MutableMapping.register(dict)
可以看到MutableMapping继承的是Mapping(不可变) ,最后把dict注册到MutableMapping.中 MutableMapping.register(dict)
可以通过 isinstance 查看一个字典的类型
from collections.abc import Mapping, MutableMapping #dict属于mapping类型 a = {} print (isinstance(a, MutableMapping))
测试结果如下
dict的常用方法
copy(这里是浅拷贝)
a = {"biao1":{"company":"imooc"}, "biao2": {"company": "imooc2"} } #copy, 返回浅拷贝 new_dict = a.copy() new_dict["bobby1"]["company"] = "imooc3"
调试结果如下
fromkeys把可迭代的对象转变为dict
new_list = ["bobby1", "bobby2"] new_dict = dict.fromkeys(new_list, {"company":"imooc"})
调试结果如下
get取值,可以设置一个默认值避免字典中keyerror异常
a = {"name1": "jack", "name2": "jane"} value = a.get("name3", "not exist")
调试结果如下
setdefault(),和get()相似,不同的是如果没有某个键名,会把此键名和默认值加入到字典中
a = {"name1": "jack", "name2": "jane"} # 没有指定键名时 a.setdefault("name3", "not exist")
调试结果如下
update(), 可用于添加字典元素
a = {"name1": "jack", "name2": "jane"} # 直接添加字典方式 a.update({"name3": "hong"}) # 使用参数名方式 a.update(name4="lilei", name5="mei")
调试结果如下
dict的子类
如果我们想要继承dict重写我们的逻辑 ,不建议继承list和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)
打印结果
应该使用python为我们提供的UserDict
from collections import UserDict class Mydict(UserDict): def __setitem__(self, key, value): super().__setitem__(key, value*2) my_dict = Mydict(one=1) pass
调试结果如下
dict 的子类defaultdict
当我们取值一个不存在的值的时候,它会返回一个 {}
from collections import defaultdict my_dict = defaultdict(dict) my_value = my_dict["bobby"]
调试结果如下
内部源码分析
set和frozenset
初始化一个set
# 使用set关键字 s1 = set('abc') # 使用{} s2 = {'a', 'b'} print(type(s1), type(s2))
向set中添加元素
s1 = set('abc') s1.add('d') print(s1)
使用update()函数更新set
s1 = set('abc') s2 = set('xy') s1.update(s2) print(s1)
difference()函数来求两个集合的差集
s1 = set('abc') s2 = set('cd') # 相当于s1 - s2 re_set = s1.difference(s2) print(re_set)
set的数学运算
s1 = set('abc') s2 = set('cd') # 差集 print(s1-s2) # 交集 print(s1 & s2) # 并集 print(s1 | s2)
issubset()判断集合A是否为集合B的子集
s1 = set('abc') s2 = set('c') print(s2.issubset(s1))
dict和set的实现原理
list和dict的性能比较
1 dict的查找性能远大于list
2 在list中随着list数据量的增大,查找相同数据量的时间也会增大
3 在dict中随着dict数据量的增大,查找相同数据量的时间不怎么受影响
dict原理小结
dict找一个值之所以快,是因为他会根据这个键,进行哈希获得偏移量,直接取值,时间复杂度为0(1)
1)dict的key或者set的值都必须是可hash的,他们的实现原理相同。不可变对象都是可hash的,比如string, tuple, fronzenset
2)dict的内存花销大,但是查询速度快;自定义的类中,只要加上魔法函数__hash__, 那么这个类就是可hash的
3)dict的存储顺序和元素添加顺序有关,因hash值冲突的原因
4)添加数据的时候有可能会改变已有数据的顺序:存储dict的时候,python会预先申请一段大于dict数据需求的连续内存空间,以减少hash冲突的概率,当添加数据量使得大于分配内存空间的1/3的时候,python就会另申请一个较大的内存空间,把原先的数据进行迁移,重新进行hash值的计算