《老鸟python 系列》视频上线了,全网稀缺资源,涵盖python人工智能教程,爬虫教程,web教程,数据分析教程以及界面库和服务器教程,以及各个方向的主流实用项目,手把手带你从零开始进阶高手之路!点击 链接 查看详情




dict类型

阅读:227572793    分享到

假设我们需要用一个数据结构来存储 3 万个汉字,如果我们使用 list 或 tuple 来存储,当我们查找某个汉字时, 就需要从第一个成员开始一直找,直到我们能找到,或者到最后一个成员没有找到为止。

dict 就是字典,其实现原理和我们使用的字典是一样的,假设我们使用 dict 来存储 3 万个汉字,我们只需要在字典的索引表里查这个汉字对应的位置,就可以找到这个字,无论找哪个字,我们都可以一次性定位到该字,也就是时间复杂度为 o(1)。 我们使用 dict 数据结构来存储汉字,查找速度不会随着汉字内容的多少而变化。

对于我们前面学过的 str 类型,list 类型和 tuple 类型都可以通过下标访问里面的成员, 而 Python 的 dict 是无序的集合(无法通过下标访问),但我们可以通过 dict 的键(key)来访问,key 就类似于有序集合的下标。

dict 的定义

dict 是 Python 内置的数据类型,在其它语言中一般属于非内置类型,是由第三方库或者程序员本身写的库, 比如 c++ 中的 stl 库中的 map 和 Python 的 dict 类似,但实现方式不一样(c++ 的 map 用的是红黑树,Python 用的是哈希表),Python 语言用 {} 来表示是一个 dict,每个成员是一个键-值(key-value)配对。

student_dict = {"name": "ruhua", "age": 18}
print(student_dict)

dict 成员在内存中的顺序和定义时候的顺序不一定一样,这是由创建哈希表的算法(大家可自行搜索哈希表算法) 决定的,哈希表的算法决定键的存放位置。

student_dict = {'a': 1, 'b': 2, 'c': 3}  # 内存中顺序为 {'a': 1, 'c': 3, 'b': 2}
print(student_dict)  #(注意:在 Python3.8 中, print 是按照用户创建 dict 的顺序输出,而不是内存顺序输出)

dict 的增删改查

我们知道 dict 是无序的,所以无法通过下标访问值,但我们可以通过 key 来访问该 key 对应的值, 访问方式为 dict[key],但要确保 key 存在,不然会报异常;我们也可以使用 dict 的 get 函数获取 key 对应的值,如果 key 不存在,则返回 None。

mydict = {'a': 1, 'b': 2, 'c': 3}
print(mydict['b'])
print(mydict.get('b'))
print(mydict['d'])      # 报异常
print(mydict.get('d'))  # 返回 None

我们如果想给字典添加一个值,可以直接通过 dict[key] = value 的方式,如果 key 已经存在的话,则是修改。

mydict = {'a': 1, 'b': 2, 'c': 3}
mydict['d'] = 4
mydict['b'] = 5
print(mydict)

因为 dict 的每个成员都是键值配对,所以删除成员的时候,通过键删除值,整个成员就会删除掉, 可以使用 Python 内置函数 del 或 dict 成员函数 pop 做删除操作,注意要确保删除的键存在,不然会报异常。

mydict = {'a': 1, 'b': 2, 'c': 3}
del mydict['b']
print(mydict)
mydict.pop('a')
print(mydict)

获取 dict 键,值,成员的函数

dict 的 keys 函数获取 dict 所有成员的键放在一个 dict_keys 类型的对象中,这个对象是一个可遍历的集合,我们可以通过遍历这个对象,获取每一个键。

mydict = {'a': 1, 'b': 2, 'c': 3}

allkeys = mydict.keys()
print(allkeys)
print(type(allkeys))
for key in allkeys:
    print(key)

dict 的 values 函数获取 dict 所有值放在一个 dict_values 类型的对象中,这个对象是一个可遍历的集合,我们可以通过遍历这个对象,获取每一个值。

mydict = {'a': 1, 'b': 2, 'c': 3}

allvalues = mydict.values()
print(allvalues)
print(type(allvalues))
for value in allvalues:
    print(value)

dict 的 items 函数获取 dict 所有的成员放在一个 dict_items 类型的对象中,这个对象是一个可遍历的集合,我们可以通过遍历这个对象,获取每一个成员。

mydict = {'a': 1, 'b': 2, 'c': 3}

allitems = mydict.items()
print(allitems)
print(type(allitems))
for item in allitems:
    print(item)

注意:在 Python 2 中,dict 还提供了 iter 系列函数(iter_keys 函数,iter_values 函数,iter_items 函数)来获取 dict 的键,值和成员,并把他们放入迭代器中;而 Python2 的 keys 函数,values 函数和 items 函数则把取出的值放在一个 list 中。

使用 dict 注意事项

dict 的键就如有序集合的索引一样,dict 成员的键是不可以修改的。

dict 成员的键就像索引一样是不可以重复的。

mydict = {'a': 1, 'b': 2, 'c': 3}
mydict['b'] = 4  # 只会修改键所对应的值
print(mydict)

因为 Python 的 dict 数据结构是采用 hash 算法构建的,hash 算法决定 key 的位置, 所以 dict 成员的键是必须可以 hash 的,所有的常量都是可 hash 的,比如数字,字符串,bool值,None 等等, 对于 tuple 来说,如果 tuple 的成员也都是可 hash 的则可以,否则不行。

mydict = {}                         # 空的 dict
mydict[-1.1] = "Hello"              # 数字
mydict["babye"] = "babye"           # 字符串
mydict[False] = "welcome"           # bool 值
mydict[None] = "Python"
mydict[(1, (2, 3))] = "老鸟python"  # 可哈希的 tuple
mydict[[1, 2]] = "my god"           # [1, 2] 不可 hash
mydict[{'a': 1}] = "my god"         # {'a': 1} 不可 hash
mydict[(1, (2, [3,4]))]             # (1, (2, [3,4]) 不可 hash

对 dict 成员的 key 进行增加,删除操作时,dict 的 hash 表的结构就会改变, 成员的存储顺序就会发生变化,在遍历的时候就会出现混乱,因为已经遍历过的成员有可能再次被遍历等等(在循环章节我们再讲这个问题)。

本节重要知识点

dict 内部存储的数据结构。

熟练使用 dict 增删改查。

熟练使用获取 key,value 和 成员的函数。

作业

著名公司(某为)笔试题:对下面数组中的元素,统计出每个元素出现的次数,要求时间复杂度为o(n)。(提示:使用字典做为辅助)

mylist = [2, "hello", "老鸟", "python", "老鸟python", "hello", 2, "hello", "python", 2]

如果以上内容对您有帮助,请老板用微信扫一下赞赏码,赞赏后加微信号 birdpython 领取免费视频。


登录后评论

user_image
Wren
2023年4月4日 10:36 回复

mylist = [2, "hello", "老鸟", "python", "老鸟python", "hello", 2, "hello", "python", 2] dict_new = {} for i in mylist: if i not in dict_new: dict_new[i]=1 else: dict_new[i]+=1


user_image
13890434939
2022年10月12日 20:16 回复

mylist = [2, "hello", "老鸟", "python", "老鸟python", "hello", 2, "hello", "python", 2] mydict = dict.fromkeys(mylist,0) for i in mylist: if i in mydict: mydict[i] += 1 print(mydict)


user_image
yyj
2020年8月4日 13:17 回复

students_dict={"姓名","性别","年龄","是否单身","父母名字"}

print(students_dict)

运行结果:

{'是否单身', '性别', '父母名字', '姓名', '年龄'}


user_image
Anonymous
2019年11月25日 14:23 回复

已经弄明白了dict字典的知识点。进入下一环节。


user_image
Liao
2019年9月8日 16:49 回复

坚持坚持


user_image
Michael李登淳
2019年8月7日 16:04 回复

在练习时有这个疑问,在网上搜出了很多知识点,感觉要学的好多。。。。。每个都要敲代码,学习进度一下子慢了好多,但是要坚持下去,毕竟敲完后成就感还是慢慢的


user_image
黄玄
2019年6月18日 07:15 回复

dict 一定要有對應的key 和value,所以不能放上去,沒有對應的value set 外表看上去和dict 都是{}表示,但是 如果用type(a)看一下就知道到底是dict 還是set

所以如果要(1,2,3)和(1,[2,3])放入set 和dict的話,set是可以放(1,2,3),但是不可以放(1,[2,3]),列表是可變對象,set 裡面的元素不能更改 否則就會


user_image
ChroniCat
2018年6月28日 14:28 回复

要理解dict的有关内容需要你理解哈希表(map)的相关基础知识,这个其实是《算法与数据结构》里面的内容。

1.list和tuple其实是用链表顺序存储的,也就是前一个元素中存储了下一个元素的位置,这样只要找到第一个元素的位置就可以顺藤摸瓜找到所有元素的位置,所以list的名字其实就是个指针,指向list的第一个元素的位置。list的插入和删除等可以直接用链表的方式进行,比如我要在第1个元素和第2个元素中间插入一个元素,那么直接在链表的最后面(我们假设这个list只有两个元素,那么也就是在第3个元素的位置上)插入这个元素,然后把第一个元素指针指向这个元素(第3个位置),然后再把新插入的元素的指针指向原来的第2个元素,这样插入操作就完成了。读取这个list的时候,先用list的名字(就是个指针,指向第1个元素的位置)找到第一个元素,然后用第1一个元素的指针找到第2个元素(位置3),然后用第2个元素的指针找到第3个元素(位置2),以此类推。所以list的顺序和内存中的实际顺序其实不一定完全对应。这种存储方式不会浪费内存,但查找起来特别费时间,因为要按照链表一个一个找下去,如果你的list特别大的话,那么要等好久才会找到结果。

2.dict则为了快速查找使用了一种特别的方法,哈希表。哈希表采用哈希函数从key计算得到一个数字(哈希函数有个特点:对于不同的key,有很大的概率得到的哈希值也不同),然后直接把value存储到这个数字所对应的地址上,比如key='ABC',value=10,经过哈希函数得到key对应的哈希值为123,那么就申请一个有1000个地址(从0到999)的内存,然后把10存放在地址为123的地方。类似的,对于key='BCD',value=20,得到key的哈希值为234,那么就把20存放在地址为234的地方。对于这样的表查找起来是非常方便的。只要给出key,计算得到哈希值,然后直接到对应的地址去找value就可以了。无论有几个元素,都可以直接找到value,无需遍历整个表。不过虽然dict查找速度快,但内存浪费严重,你看我们只存储了两个元素,都要申请一个长度为1000的内存。

3.现在你知道为啥key要用不可变对象了吧?因为不可变对象是常量,每次的哈希值算出来都是固定的,这样就不会出错。比如key='ABC',value=10,存储地址为123,假设我突发奇想,把key改成'BCD',那么当查找'BCD'的value的时候就会去234的地址找,但那里啥也没有,这就乱套了。

3.你看我们上面有一句话:对于不同的key,有很大的概率得到的哈希值也不同。那么有很小的概率不同的key可以得到相同的哈希值了?没错,比如对于我们的例子来说,哈希值只有3位,那么只要元素个数超过1000,就一定会有至少两个key的哈希值相同(鸽笼原理),这种情况叫“冲突”,设计哈希表的时候要采取办法减少冲突,实在冲突了也要想办法补救。不过这是编译器的事情,况且对于初学者的我们来说碰到的冲突的概率基本等于零,就不用操心了。


user_image
黍离
2018年7月19日 14:59

感谢细致的讲解!


user_image
M3小蘑菇
2018年12月22日 16:00

只知道比特币似乎有用哈希函数


user_image
freezerush
2019年12月4日 07:47

十分感谢分享!


user_image
Narc
2018年4月16日 21:26 回复

这一节好难理解。。。