Python进阶笔记(四)深入Python的set和dict

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中的哈希表查找过程:
在这里插入图片描述

总结:

1. dict的key或者set的值 都必须是可以hash的,不可变对象都是可hash的, str, fronzenset, tuple,自己实现的类修改 hash
2. dict的内存花销大(会由空余的表元),但是查询速度快, 自定义的对象 或者python内部的对象都是用dict包装的;
3. dict的存储顺序和元素添加顺序有关
4. 添加数据有可能改变已有数据的顺序
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章