PythonI/O进阶学习笔记_5.python的set和dict

前言:

我一直觉得对我来说学习知识很忌讳不系统。本篇内容与上一篇 自定义序列类是有联系的。

上一篇比较通范的了解了序列类的一些协议和特性,并且有些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:不可变集合(无序,不重复,不可变)

frozenset没有改变的方法 一旦初始化了 就不可变了。其中,不可变类型对可变类型的一个特点,不可变类型是可以作为dict的key的。
 
b.set常用操作
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的散列表

特点:

- dict的key必须是可hash的,不可hash的是不能当成dict的key的,比如list
- python申请内存的时候,会初始化一个小的连续的空空间
- 由上一条可得这样 一定会存在浪费,这时候每次变动操作 会计算剩余空间,剩余空间小于1/3的时候,会重新生成空间并且将现在数据都拷贝过去(rehash)
- 数组比这种链表结构好的地方,就是可以直接根据偏移量来存取,而不用全部开始从头遍历。
(这种结构hash和重hash的策略用在很多地方,包括redis等)
 
b.存储结构了解了,那么数据的查找过程呢?

先计算a的散列值,查找表源是否为空,

因为a是不变的,所以如果表源为空,那么就会抛出key error。

如果表源不为空,也有可能是其他key,查看key是否是要查找的key。

如果是其他key,重新散列循环查找。

 

c.这种hash结构在python中的特点

- 我们可以用__hash__这个魔法函数实现可hash对象

 - dict内存开销比较大,这是hash表的特点。

- 实际上python内部类和对象,都是dict。

- dict存储顺序和元素添加顺序有关。

- 插入数据后检查剩余空间引发的重hash,会影响原来的数据(比如地址)。

posted @ 2019-09-17 00:57  besttr1225  阅读(398)  评论(2编辑  收藏  举报