数据类型之字典
一、概要
- 字典基础
- 字典基本操作
- 字典方法
- 字典总结
二、字典基础
(一)创建字典
一个字典通过一对大括号进行创建,键值之间使用(:)进行分隔,每一对键值之间使用(,)分隔,如果大括号中无值那么就是空字典;它是Python中唯一具有映射关系的数据类型,效率高于列表。
d = {"name":"bright",12:["jskfj",23]} #键唯一且为不可变数据类型
当然如果键值不唯一,也不会抛出异常,只不过相同的键会被覆盖掉。
d = {"name":"bright",12:["jskfj",23],"name":"gh"} #键唯一且为不可变数据类型
print(d)
#-----------------------------------------输出———————————————
{12: ['jskfj', 23], 'name': 'gh'}
(二)dict函数
可以使用dict函数通过其它映射进行字典的创建
items=[('name','张三'),['iphone',1872563541]]
d=dict(items)
print(d) #{'name': '张三', 'iphone': 1872563541}
可以看出,为dict函数传入一个列表,而列表的元素就是一个个的列表或者元组,元素中包含两个值,第一个值表示了字典的键,第二个值表示字典的值。
另外dict也可以通过关键字传参,创建字典。
d=dict(name='张三',iphone=1872563541)
print(d) #{'iphone': 1872563541, 'name': '张三'}
如果dict函数不指定任何参数,就会创建一个空字典
d=dict()
print(d)#{}
三、字典基本操作
(一)len(dict)
返回字典中元素(键值对)的个数
d = {"name":"bright",12:["jskfj",23],"name":"gh"}
print(len(d))#2
(二) dict[key]
#获取键对应的值
d = {"name":"bright",12:["jskfj",23],"name":"gh"}
result=d["name"]
print(result)#gh
#对键进行赋值
d = {"name":"bright",12:["jskfj",23],"name":"gh"}
d["name"]="root"
print(d)#{12: ['jskfj', 23], 'name': 'root'}
(三) del dict[key]
d = {"name":"bright",12:["jskfj",23],"name":"gh"}
del d["name"]
print(d)#{12: ['jskfj', 23]}
(四) key in dict
判断dict中是否含有键为key的项
d = {"name":"bright",12:["jskfj",23],"name":"gh"}
print("name" in d)#True
(五) 字典格式化字符串
在字符串的格式化中,已经有很多方法了,这里也可以通过字典进行格式化,引进format_map()方法。
定义格式化参数字典:
data={
"name":"bright",
"age":27,
"hobby":"music"
}
定义字符串模板:
str="""
{name} is a boy,{age} years old,liking {hobby}.
"""
使用format_map()方法格式化字符串:
print(str.format_map(data))#bright is a boy,27 years old,liking music.
四、字典方法
(一) clear()
清空字典中的所有元素
d = {"name":"bright",12:["jskfj",23],"name":"gh"}
d.clear()
print(d)#{}
(二) copy()与deepcopy()
copy()方法复制的字典只是浅复制,复制的是第一层的字典数据。也就是说修改原字典还是新字典,对应的元素都会改变。
d = {"name":"bright",12:["jskfj",23]}
d1=d.copy()
print(d1)
print(d)
############输出###########################
{12: ['jskfj', 23], 'name': 'bright'}
{12: ['jskfj', 23], 'name': 'bright'}
修改原字典中的值:
d = {"name":"bright",12:["jskfj",23]}
d1=d.copy()
d["name"]="alb"
print(d1)
print(d)
#################输出################
{12: ['jskfj', 23], 'name': 'bright'}
{12: ['jskfj', 23], 'name': 'alb'}
修改copy后新字典中的值:
d = {"name":"bright",12:["jskfj",23]}
d1=d.copy()
d1["name"]="sjp"
print(d1)
print(d)
###############输出###################
{12: ['jskfj', 23], 'name': 'sjp'}
{12: ['jskfj', 23], 'name': 'bright'}
修改第二层数据:
d = {"name":"bright",12:["jskfj",23]}
d1=d.copy()
d[12][0]=56
print(d)
print(d1)
################输出###############
{12: [56, 23], 'name': 'bright'}
{12: [56, 23], 'name': 'bright'}
从上面可以看到修改第二层数据,无论是修改新字典还是原字典,另外一个字典的值都会改变,如果想改变这种情况,就需要进行深拷贝。
from copy import deepcopy
d = {"name":"bright",12:["jskfj",23]}
d1=deepcopy(d)
d[12][0]=78
print(d)
print(d1)
###################输出##################
{12: [78, 23], 'name': 'bright'}
{12: ['jskfj', 23], 'name': 'bright'}
(三) fromkeys()
用于根据key建立新的字典,该方法的返回值就是新的字典。在新的字典中所有的key都有相同的默认值None,不过可以根据fromkeys方法的第二个参数进行指定默认值。
#在新字典上调用fromkeys创建新的字典,通过列表指定key
dic={}.fromkeys(['name','age','hobby'])
print(dic) #'hobby': None, 'age': None, 'name': None}
#通过元组指定key
dic={}.fromkeys(('name','age','hobby'))
print(dic) #'hobby': None, 'age': None, 'name': None}
通过fromkeys的第二个参数指定默认值
dic={}.fromkeys(('name','age','hobby'),'bx')
print(dic) #{'hobby': 'bx', 'age': 'bx', 'name': 'bx'}
(四) get()
获取对应key的value,使用dict[key]可以获取值,但是一旦key不存在就会抛出异常,get()方法就不会抛出异常,如果key不存在,会返回None值。当然也可以通过get的第二个参数指定传入的默认值。
d = {"name":"bright",12:["jskfj",23]}
print(d.get("name"))#bright
print(d.get("age"))#None
(五) items()与keys()
在字典的基本操作中已经说明了这两种方法,items方法用于获取字典的key-value,返回值是一个可迭代的dict_items类型;keys方法用于返回字典中所有的key,是dict_keys类型,也是可迭代的。
d = {"name":"bright",12:["jskfj",23]}
result1=d.items()
result2=d.keys()
print(type(result1))
for key,val in result1:
print(key,val)
print(type(d.keys()))
for key in result2:
print(key)
#########输出############
<class 'dict_items'>
12 ['jskfj', 23]
name bright
<class 'dict_keys'>
12
name
(六) pop()和popitem()
pop方法和popitem方法用于弹出字典中的元素,pop方法用于获取指定的key的值,并从字典中弹出这个key-value,popitem方法用于返回最后一个key-value,并且弹出这对key-value。
#pop方法dict={'name':'xiaoli','depart':'xiaoshou','age':27}
print(dict.pop('name'))#xiaoli
print(dict)#{'age': 27, 'depart': 'xiaoshou'}
#popitem方法
dict={'name':'xiaoli','depart':'xiaoshou','age':27}
print(dict.popitem())#('age', 27)
print(dict)#{'name': 'xiaoli', 'depart': 'xiaoshou'}
(七) setdefault()
setdefault方法用于设置key的默认值,该方法接收两个参数,第一个表示key,第二个表示默认值,如果key在字典中不存在,那么就会向字典中添加key,并用第二个参数作为值(参数未指定就为None),如果字典中已经存在key,setdefault不会修改key原来的值,而且该方法会返回原来的值。
dict={}
dict.setdefault("name","menb")
dict.setdefault("name","niko")
dict.setdefault("age")
print(dict)
##############输出########
{'age': None, 'name': 'menb'}
其实这个方法与dict[key]=value差不多,但是这个方法可以添加元素,但是不能修改元素,因为已经存在的key,它只会返回不会修改,而dict[key]=value既可以添加又可以修改。
(八) update()
update方法用于一个字典中的元素更新另外一个字典。该方法接收一个参数,该参数表示用作更新数据的字典的数据源。列如:
d1.update(d2)
将d2中的元素更新到d1中,如果d2中的key-value对在d1中不存在,就会在d1中新添加key-value对,如果d2中的key在d1中已经存在,那么就会把d2中的key的value更新给d1中key的value。
d1={"name":"bright","age":27,"hobby":"music"}
d2={"name":"sansha","city":"陕西"}
d1.update(d2)
print(d1)#{'city': '陕西', 'hobby': 'music', 'age': 27, 'name': 'sansha'}
该方法试讲两个列表进行合并,当然还有其它方法,使用dict(dict1,**dict2):
>>> dict1 = {'name':'bright','age':28}
>>> dict2 = {'identified':'worker'}
>>> dict(dict1,**dict2)
{'age': 28, 'name': 'bright', 'identified': 'worker'}
(九) values()
获取字典中的值,返回的是dict_values类型,是可迭代的。它与keys方法返回的有一点不一样就是,它返回的值得列表可以有重复的,而keys()方法返回的都是唯一值的列表。
dict={"name":"bright","age":27,"hobby":"music"}
result=dict.values()
print(type(result))
for val in result:
print(val)
##########输出###########
<class 'dict_values'>
music
27
bright
五、字典总结
对字典操作最多的无非就是增、删、查、改这些基本操作,现在就对这些基本操作总结一下。
d = {"name":"bright",12:["jskfj",23]}
# #查
print(d["name"])
print(d.get("name",None)) #推荐
##增加
# d["age"] = "123"
# d.default("age","123")
print(d)
#修改
# d["name"] = "zhangfei"
#删除
# ret = d.pop(12)
# del d[12]
# print(ret)
##更新
d1 = {"name":"alia",12:["jskfj",23]}
d2 = {"name":"megon",12:["jskfj",23],"age":32}
d2.update(d)
print(d2)
注意:
字典的键可以是任意不可变类型,例如,元组、字符串等。
六、字典内存模型
画图
七、有序字典
如何解决字典有序问题 python3.6之后是有序
如果之前的版本使用OrderDict使其有序:
from collections import OrderedDict
d=OrderedDict()
d['foo']=1
d['bar']=2
d['spam']=3
d['grok']=4
for key in d:
print(key,d[key])
八、字典的键为什么不可变
首先看一个例子:
dict1 = {"key1":"val1", 1:"val2",[1,2,3]:"val3"}
请问上面这个字典有什么问题吗?
显然会有问题,因为列表时可变的,无法哈希的。
这与字典底层的存储是分隔不开的,列表底层使用的是线性表,是一块连续的内存空间,比如:
list1 = [1,2,3] # 通过索引取值,复杂度为o(1);查找具体的值是o(n)
如果需要查找3,在不知道索引的情况下,就通过遍历列表的元素进行比较,复杂度为o(n)。那么字典是如何在不通过遍历的情况下找到对应元素呢?
d1 = {1:"a", 2:"b", 3:"c"}
如上在d1中查找最后一个一个键值对。
首先它需要将上面的数据进行存储,它申请了一块空间,类似就是列表或者数组一样的。
1 % 3 = 1
2 % 3 = 2
3 % 3 = 0

等到取值时,就可以通过上面的计算方式可以得到对应值在这个表中的下标,然后取值。
而上面的表就是哈希表,那么什么时哈希表呢?
哈希表是基于键、值对存储的数据结构,底层一般采用的是列表(数组)。
而基于键计算的下标的过程就是通过哈希函数来实现的。
哈希函数的功能:提供把关键字映射到列表中的位置算法,是哈希表存储数据的核心所在。
哈希算法决定了数据的最终存储位置,不同的哈希算法设计方案,也关乎哈希表的整体性能,所以,哈希算法就变得的尤为重要。
那么,如果字典是这样的话:
d1 = {'ab':1, 'bcd':2, 'ca':3}
# 简单哈希函数
a = 1
b = 2
...
z = 26
ab = 1 + 2 = 3
bcd = 2 + 3 + 4 = 9
ca = 3 + 1 = 4
# 计算位置 对个数取余
3 % 3 = 0
9 % 3 = 0
4 % 3 = 1
# 之后与上面的一样
实际上哈希函数的算法有很多。
实例:
def hash_code(key):
# 设置字母 A 的在字母表中的位置是 1
a = 1
b = 2
c = 3
if key == 'ab':
res = a + b
elif key == 'ac':
res = a + c
else:
res = b + c
return res % 3
hash_table = [None] * 3
# 保存所有学生
idx = hash_code('ab')
hash_table[idx] = '张三'
idx = hash_code('bc')
hash_table[idx] = '李四'
idx = hash_code('ac')
hash_table[idx] = '王五'
print('哈希表中的数据:', hash_table)
另外Python中有内置的hash函数,专门将字符串转成整数。而这个hash的过程就是不可变的,因为下标不能变动,否则取值就不正确。