python 3 字典整理
字典:在元组和列表中,都是通过编号进行元素的访问,但有的时候我们按名字进行数据甚至数据结构的访问,在c++和java中有map的概念,也就是映射。同样在python中也提供了内置的映射类型–字典。映射其实就是一组键(key)和
值(value)以及之间的映射函数。键可以是数字、字符串甚至是元组。
字典的每个键值(key=>value)对用冒号(:)分割,每个对之间用逗号(,)分割,整个字典包括在花括号({})中,
键必须是唯一的,但值则不必。
或者通过dict({key:value})的方式创建字典
值可以取任何数据类型,但键必须是不可变的,如字符串,数字或元组。 #那么什么是可变数据类型和不可变数据类型的深入理解呢,看我另外一篇文章 https://www.cnblogs.com/clschao/articles/9110119.html
格式如下所示:
d = {key1 : value1, key2 : value2 }
dict = {'Alice': '2341', (1,2): '9102',98.6: '3258'}
细心的同学应该就发现了,其中的键的类型有字符串、元祖、数字。
一、字典的特性
a、字典是一种可变容器模型,且可存储任意类型对象(增删改不会改变该字典在存储空间中的位置)
d1 = {'a':1,(1,3):2,'c':3}
print(id(d1))
#增加
d1['d'] = 'what'
print(d1)
print('增加>>',id(d1))
#删除
del d1['a']
print(d1)
print('删除>>',id(d1))
#修改
d1[(1,3)] = '元祖'
print(d1)
print('修改>>',id(d1))
b、字典是无序的
#无序的例子:
d1 = {'a':1,'b':2,'c':3} print(d1) #结果:{'a': 1, 'c': 3, 'b': 2} #结果:{'b': 2, 'c': 3, 'a': 1}
c、字典是通过key来作为键也就是所说的索引,序列类型只用数字类型的键(从序列的开始按数值顺序索引);
还有两点需要强调一下:
d、不允许同一个键出现两次。创建时如果同一个键被赋值两次,后一个值会被记住,这也就是我们所说的键要唯一,如下实例:
dict = {'Name': 'Runoob', 'Age': 7, 'Name': '金老板'}
print ("dict['Name']: ", dict['Name'])
#dict['Name']: 金老板
e、键必须不可变,所以可以用数字,字符串或元组充当,而用列表就不行,如下实例:
dict = {['Name']: 'Runoob', 'Age': 7}
print ("dict['Name']: ", dict['Name'])
输出结果:
raceback (most recent call last):
File "test.py", line 3, in <module>
dict = {['Name']: 'Runoob', 'Age': 7}
TypeError: unhashable type: 'list' #list是不可hash的类型,这个一会在原理的地方给大家讲,现在讲也可以
那么理解了可变和不可变的数据类型之后,为什么说只能是用不可变的数据类型来作为键呢,那么和咱们刚才说的映射函数有关系,就是hash函数,这样也是字典为什么有这几个特性的原因,下面我们说一下hash函数和hash表
哈希表 (hash tables)
哈希表(也叫散列表),根据关键值对(Key-value)而直接进行访问的数据结构。它通过把key和value映射到表中一个位置来访问记录,这种查询速度非常快,更新也快。而这个映射函数叫做哈希函数,存放值的数组叫做哈希表。
hash函数通过对key进行计算找到一个哈希表中的位置,将key对应的值的内存地址(引用)放上,
看一段代码:
d1 = {'a':1,'b':2,'c':'bbb','d':4,'e':1000}
print(id(d1['a']))
#1745486288 #print两次还是这个值
print(id(d1['a'])) #1745486288 #1745486288
print(id(d1['c'])) #2690603927904 #1482254829920
print(id(d1['e'])) #2690738895088 #1482389858544
哈希函数的实现方式决定了哈希表的搜索效率。具体操作过程是:(有很多种方法:直接定址法、除法取余法、数字分析法、平方取中法、折叠法等等多种hash方法,而这些方法要满足两点1、尽量分散,不用计算出太多重复的值 2、尽可能简单快速,别让我等你1小时,对不对,哈哈,下面用除法取余法来看一下,这些方法等以后有多余的精力了再去研究。)
- 数据添加:把key通过哈希函数转换成一个整型数字,然后就将该数字对数组长度进行取余等等一些复杂的计算,计算结果就当作数组的下标,将value存储在以该数字为下标的数组空间里。
- 数据查询:再次使用哈希函数将key转换为对应的数组下标,并定位到数组的位置获取value。
- 通过上面我们能看出计算中应用到了数组的长度,那么如果用可变的数据类型作为key的话,如果数据发生了变化,增加或者减少,那么hash的结果是不是就不能和之前存储的内容对应上了。所以必须用不可变的数据类型。
- 那同时是不是也说明了键不能重复,如果重复了按照程序从上到下从左至右的顺序来编译执行的话,那么如果有两个重复的键,是不是hash的结果是不是就相同了,那么后者就将将前者的数据覆盖掉,那你就丢数据了
- 那么每次在执行程序的时候,都要对键进行hash,hash结果有大有小,和hash函数的内部逻辑有关系,可以说每次hash出来的结果是不同的,那么就将该键对应的值放到了hash表中的散
- 乱位置,这样,我们在每次打印整个字典的时候,实际上是对整个hash表进行遍历,找到hash中部位null的entry,这样就不能保证顺序了。这就是无序的原理。
但是,对key进行hash的时候,不同的key可能hash出来的结果是一样的,尤其是数据量增多的时候,这个问题叫做哈希冲突。如果解决这种冲突情况呢?通常的做法有两种,一种是链接法,另一种是开放寻址法,Python选择后者。
开放寻址法(open addressing)
开放寻址法中,所有的元素都存放在散列表里,当产生哈希冲突时,通过一个探测函数计算出下一个候选位置,如果下一个获选位置还是有冲突,那么不断通过探测函数往下找,直到找个一个空槽来存放待插入元素。
另外还有字典的搜索策略设置还有字典对象缓冲池等内容,这些太深入的内容我们不讲,因为不适合你们现阶段的学习,等将来实际应用,开发编程没有问题的时候,自己再去做深入的了解和分析。
在这里呢我引出一个其他的知识点小整数对象池:
Python里一切都是对象.所以1,2,3,4…这些整数也都是对象。这些基本的不可变对象在python里会被频繁的引用,创建,如果不找到好的办法的话很容易让python引发效率瓶颈,所以python引入了整数对象池的机制(参考intobject.c中代码片段)
代码段中,清楚写了[-5, 256) 这些小整数被定义在了这个对象池里.所以当引用小整数时会自动引用整数对象池里的对象。
看一段代码:
a1 = 1000 a2 = 1000 print(id(a1)) #2805846780144 #2008693487856 #两次不同 print(id(a2)) #2805846780144 #2008693487856 #a1 和a2 相同 ,因为是同一个数据的引用 print(a1 is a2) #True a = 1.2 print(id(a)) #2805858369608 #2008692768728 aaa = 1 print(id(aaa)) #1739391440 #1739391440 #两次相同
二、字典的操作
1、基本操作
(1)len(dict) :计算字典元素个数,即键的总数。
>>> dict = {'Name': 'Runoob', 'Age': 7, 'Class': 'First'}
>>> len(dict)
3
(2)str(dict):输出字典,以可打印的字符串表示。
>>> dict = {'Name': 'Runoob', 'Age': 7, 'Class': 'First'}
>>> str(dict)
"{'Name': 'Runoob', 'Class': 'First', 'Age': 7}"
(3)type(dict):返回输入的变量类型,如果变量是字典就返回字典类型。
>>> dict = {'Name': 'Runoob', 'Age': 7, 'Class': 'First'}
>>> type(dict)
<class 'dict'>
(4)查询:
dict = {'Name': 'Runoob', 'Age': 7, 'Class': 'First'}
print ("dict['Name']: ", dict['Name'])
print ("dict['Age']: ", dict['Age'])
输出结果
dict['Name']: Runoob
dict['Age']: 7
dict = {'Name': 'Runoob', 'Age': 7, 'Class': 'First'}
s1 = dict['Name']
s2 = dict.__getitem__('Name') #这是后面会讲到的
print(s1)
print(s2)
如果用字典里没有的键访问数据,会输出错误如下: dict = {'Name': 'Runoob', 'Age': 7, 'Class': 'First'}; print ("dict['Alice']: ", dict['Alice']) 报错: Traceback (most recent call last): File "test.py", line 5, in <module> print ("dict['Alice']: ", dict['Alice']) KeyError: 'Alice'
get 语法:dict.get(key, default=None)
dict = {'Name': 'Runoob', 'Age': 27}
print ("Age 值为 : %s" % dict.get('Age'))
print ("Sex 值为 : %s" % dict.get('Sex', "NA"))
结果:
Age 值为 : 27
Sex 值为 : NA
(5)修改和添加
dict = {'Name': 'Runoob', 'Age': 7, 'Class': 'First'}
dict['Age'] = 8; # 更新 Age
dict['School'] = "小猪" # 添加信息
print ("dict['Age']: ", dict['Age'])
print ("dict['School']: ", dict['School'])
setdefault()
dict.setdefault(key, default=None)
key -- 查找的键值。
default -- 键不存在时,设置的默认键值
如果 key 在 字典中,返回对应的值。如果不在字典中,则插入 key 及设置的默认值 default,并返回 default ,default 默认值为 None。
dict = {'Name': 'Runoob', 'Age': 7}
print ("Age 键的值为 : %s" % dict.setdefault('Age', None))
print ("Sex 键的值为 : %s" % dict.setdefault('Sex', None))
print ("新字典为:", dict)
update()
dict.update(dict2)
dict2 -- 添加到指定字典dict里的字典。
该方法没有任何返回值。
dict = {'Name': 'Runoob', 'Age': 7}
dict2 = {'Sex': 'female' }
dict.update(dict2)
print ("更新字典 dict : ", dict)
#更新字典 dict : {'Name': 'Runoob', 'Age': 7, 'Sex': 'female'}
(6)删除
dict = {'Name': 'Runoob', 'Age': 7, 'Class': 'First'}
del dict['Name'] # 删除键 'Name'
dict.clear() # 清空字典
del dict # 删除字典
其他方法举例:
copy: #关于深浅拷贝,参考我另外一篇文章 https://www.cnblogs.com/clschao/articles/9107496.html
>>> student_id={'peace':'201421014960','nick_peace':'201421014970','pp':['p1','p2','p3']}
>>> stucopy=student_id.copy()
>>> stucopy['peace']='22222'
>>> stucopy['pp'].remove('p2')
>>> stucopy
{'peace': '22222', 'nick_peace': '201421014970', 'pp': ['p1', 'p3']}
>>> student_id
{'peace': '201421014960', 'pp': ['p1', 'p3'], 'nick_peace': '201421014970'}
>>>
deepcopy
>>> from copy import deepcopy >>> student_id={'peace':'201421014960','nick_peace':'201421014970','pp':['p1','p2','p3']} >>> stucopy=deepcopy(student_id) >>> stucopy['peace']='22222' >>> stucopy['pp'].remove('p2') >>> stucopy {'peace': '22222', 'nick_peace': '201421014970', 'pp': ['p1', 'p3']} >>> student_id {'peace': '201421014960', 'pp': ['p1', 'p2', 'p3'], 'nick_peace': '201421014970'}
fromkeys 语法:dict.fromkeys(seq[, value])
seq = ('name', 'age', 'sex') dict = dict.fromkeys(seq) print ("新的字典为 : %s" % str(dict)) dict = dict.fromkeys(seq, 10) print ("新的字典为 : %s" % str(dict)) #新的字典为 : {'age': None, 'name': None, 'sex': None} #新的字典为 : {'age': 10, 'name': 10, 'sex': 10}
in方法
dict = {'Name': 'Runoob', 'Age': 7}
# 检测键 Age 是否存在
if 'Age' in dict:
print("键 Age 存在")
else :
print("键 Age 不存在")
dict.keys() dict.values() dict.items() #这个就不举例了
pop() :Python 字典 pop() 方法删除字典给定键 key 所对应的值,返回值为被删除的值。key值必须给出。 否则,返回default值。
pop(key[,default]) key: 要删除的键值 default: 如果没有 key,返回 default 值 >>> site= {'name': '菜鸟教程', 'alexa': 10000, 'url': 'www.runoob.com'} >>> pop_obj=site.pop('name') >>> print(pop_obj) 菜鸟教程
popitem():
Python 字典 popitem() 方法随机返回并删除字典中的一对键和值(一般删除末尾对)。
如果字典已经为空,却调用了此方法,就报出KeyError异常。
site= {'name': '菜鸟教程', 'alexa': 10000, 'url': 'www.runoob.com'}
pop_obj=site.popitem()
print(pop_obj)
print(site)
#('url', 'www.runoob.com') #注意这是一个元祖
#{'name': '菜鸟教程', 'alexa': 10000}
sorted() 排序
dict = {'Name': 6, 'Age': 7, 'Class': 10}
print(sorted(dict)) #['Age', 'Class', 'Name']
print(sorted(dict.keys())) #['Age', 'Class', 'Name']
print(sorted(dict.values())) #[6, 7, 10]
print(sorted(dict.items())) #[('Age', 7), ('Class', 10), ('Name', 6)]
for 循环:
dict = {'Name': 6, 'Age': 7, 'Class': 10}
for i in dict:
print(i)
#Name
#Class
#Age
for i in dict.keys():
print(i)
#Name
#Class
#Age
for i in dict.values():
print(i)
#6
#10
#7
for k,v in dict.items():
print(k,v)
#Age 7
#Class 10
#Name 6

浙公网安备 33010602011771号