前言:
我一直觉得对我来说学习知识很忌讳不系统。本篇内容与上一篇 自定义序列类是有联系的。
上一篇比较通范的了解了序列类的一些协议和特性,并且有些list的内容。这篇更加具体到set和dict这两个序列类。
以此来了解python序列类的具体应用。(这篇比较简单)(感觉具体比抽象都更容易理解,但是也要学会思考把具体对象抽象化来表达,即提取共性)
content:
1.dict在abc中的序列类型和继承关系
2.dict实现了的常用方法
3.我可不可以继承dict这种序列类?
4.set和frozenset
5.set和dict的原理
==============
1.dict在abc中的序列类型和继承关系
dict在collection.abc中,实际上是属于MutableMapping(可变mapping)类型。
跟上篇对可变序列类继承的分析一样,MutableMapping继承了Mapping的一些功能并且加了一些可变的特性,
Mapping继承了Collection。接下来的继承和上篇的一样。
2.dict实现了的常用方法
如果用的是pycharm,还是用ctrl+b就能跳到python对dict的定义。
常用:
a = {"1":{"a":"aa"}, "2":{"b":"bb"}} # 清空字典 a.clear() # 浅拷贝字典 浅拷贝虽然可以正常赋值,但是如果 my_dopy_dict 中的值进行了改变,则 a 中的值也会进行对应的改变 my_dopy_dict = a.copy() # 深拷贝 深拷贝则是实实在在的在内存当中声明了一个新的变量 import copy new_dict = copy.deepcopy(a) # get函数 dict.get(要查找的key,如果没找到对应key的内容返回的数据) print(a.get("3",{1:"3"})) # {1: '3'} # dict.fromkeys() 函数用于创建一个新字典,以序列 seq 中元素做字典的键 seq可以是可迭代的,value 为字典所有键对应的初始值。 my_list = [1, 2, 3] my_new_dict = dict.fromkeys(my_list, {"222":"3434"}) #{1: {'222': '3434'}, 2: {'222': '3434'}, 3: {'222': '3434'}} # setdefault() 函数和 get()方法 类似, # 如果键不存在于字典中,将会添加键并将值设为默认值。 # 如果存在,则将会返回该key对应的value a.setdefault("3", "cc") # a= {'1': {'a': 'aa'}, '2': {'b': 'bb'}, '3': 'cc'} print(a.setdefault("2", "cc")) # 返回{'b': 'bb'} # update() 函数把字典dict2的键/值对更新到dict里。 # 如果字典b中有与a相同的key,则会把a中的key对应的value进行更新 # 如果字典b中有a中没有的key,则a会将未有的key与value添加进去 b = {"3": "cc", "2": "dd"} a.update(b) print(a) # {'1': {'a': 'aa'}, '2': 'dd', '3': 'cc'}
3.我可不可以继承dict这种序列类?(dict的子类)
a.如果我偷懒想实现dict这种类型,能不能直接继承这种序列类呢?同理list是否可以?
例:继承dict,并且重写设置dict key的value时调用的魔法函数,使其值变为2倍
class Mydict(dict): def __setitem__(self, key, value): super().__setitem__(key, value*2) a=Mydict(b=1) print(a) a['b']=1 print(a)
输出:
可以发现,原来同样功能和效果的,我们重写方法后,第一种方法去设置key的value值这一操作并没有调用我们重写的方法。
所以并不建议去继承python的这种序列类。
b.有没有什么办法我实在想继承?
python里专门给了个UserDict类,可以实现想要的继承Dict类的效果
from collections import UserDict class Mydict(UserDict): def __setitem__(self, key, value): super().__setitem__(key, value*2) mydict = Mydict(one = 1) # {'one': 2} 调用__setitem__这个魔法函数 mydict["one"] = 2 # {'one': 4} 这种方式也可以调用__setitem__
输出:
c.python中Dcit实际也有子类实现:defaultdict
使用:
from collections import defaultdict # 这个是dict的子类 mydict = defaultdict(dict) myvalue = mydict["bai"] # 如果不存在的话,返回{}
输出:
4.set和frozenset
a.两者是啥有啥特点?
set:集合(无序,不重复,可变)
frozenset:不可变集合(无序,不重复,不可变)
a=set('abcdee') a.add('f') print(a) another_set=set('defgh') #添加数据 #a.update(another_set) #print(a) #集合的差集 re_set=a.difference(another_set) #减法实现于__ior__魔法函数 re_set2=a-another_set #集合的交集& re_set3=a&another_set #集合的并集| re_set4=a|another_set print(re_set) print(re_set2) print(re_set3) print(re_set4) #也可以用if in判断(实现于__contains__魔法函数) if 'a' in re_set: print('I am a set')
5.set和dict的原理
之前就提过,set的性能棒的。dict的查询性能远比起list要好。
并且list中随着list数据的增大,查找时间会增大,而dict不会。
这是为什么呢?
因为dict使用hash这种数据结构存储。set也是。
a.dict的散列表
特点:
先计算a的散列值,查找表源是否为空,
因为a是不变的,所以如果表源为空,那么就会抛出key error。
如果表源不为空,也有可能是其他key,查看key是否是要查找的key。
如果是其他key,重新散列循环查找。
c.这种hash结构在python中的特点
- 我们可以用__hash__这个魔法函数实现可hash对象
- dict内存开销比较大,这是hash表的特点。
- 实际上python内部类和对象,都是dict。
- dict存储顺序和元素添加顺序有关。
- 插入数据后检查剩余空间引发的重hash,会影响原来的数据(比如地址)。