字典是键值对的无序可变序列,字典中的每个元素都是一个键值对,包含键对象和值对象。可以通过键对象实现快速获取、删除、更新对应的值对象。
字典的创建
通过{}、dict()来创建字典对象
a = {'name':'CSDN','age':18,'job':'程序猿'}
# a = {'name': 'CSDN', 'age': 18, 'job': '程序猿'}
b = dict(name='CSDN',age=18,job='程序员')
# b = {'name': 'CSDN', 'age': 18, 'job': '程序员'}
e = dict([("name","gaoqi"),("age",18)])
# e = {'name': 'gaoqi', 'age': 18}
c = {} #空的字典对象
# c = {}
d = dict() #空的字典对象
# d = {}
通过zip()创建字典对象
k = ['name','age','job']
v = ['CSDN',18,'程序员']
d = dict(zip(k,v))
print(d) #{'name': 'CSDN', 'age': 18, 'job': '程序员'}
通过fromkeys创建值为空的字典
f = dict.fromkeys(['name','age','job'])
print(f) #结果:{'name': None, 'age': None, 'job': None}
字典元素的访问
通过键获得值,若不存在,则抛出异常
a = {'name':'CSDN','age':18,'job':'程序猿'}
b = a['name']
print(b) # CSDN
通过get()方法获得值。推荐使用,优点:如果指定的键不存在,返回None;也可以设定指定键不存在时默认返回的对象。
a = {'name':'CSDN','age':18,'job':'程序猿'}
b = a.get('name')
c = a.get('gender')
d = a.get('address','China')
print(b) # CSDN
print(c) # None
print(d) # China
列出所有的键值对
a = {'name':'CSDN','age':18,'job':'程序猿'}
b = a.items()
print(b) # dict_items([('name', 'CSDN'), ('age', 18), ('job', '程序猿')])
列出所有的键,列出所有的值
a = {'name':'CSDN','age':18,'job':'程序猿'}
k = a.keys()
v = a.values()
print(k) # dict_keys(['name', 'age', 'job'])
print(v) # dict_values(['CSDN', 18, '程序猿'])
len()键值对的个数
a = {'name':'CSDN','age':18,'job':'程序猿'}
print(len(a)) # 3
检测一个键在字典中是否存在
a = {'name':'gaoqi','age':18,'job':'programmer'}
print("name" in a) #True
print("gender" in a) # False
字典元素添加、修改、删除
给字典新增键值对。如果键已经存在,则覆盖旧的键值对。如果键不存在,则新增键值对
a = {'name':'CSDN','age':18,'job':'程序猿'}
a['address']='China'
a['age']=20
print(a)
#{'name': 'CSDN', 'age': 20, 'job': '程序猿', 'address': 'China'}
使用update()将新字典中所有键值对全部添加到就字典对象上。如果key有重复,则直接覆盖
a = {'name':'CSDN','age':18}
b = {'name':'竹筒饭','gender':'男'}
a.update(b)
print(a) # {'name': '竹筒饭', 'age': 18, 'gender': '男'}
print(b)
字典中的元素的删除,可以使用del()方法;或者clear()删除所有键值对;pop()删除指定键值对,并返回对应的值对象。
a = {'name':'CSDN','age':18}
del(a['name'])
print(a) # a = {'age':18}
age = a.pop('age')
print(age) # 18
popitem() :随机删除和返回该键值对。字典是“无序可变序列”,因此没有第一个元素、最后一个元素的概念;popitem弹出随机的项,因为字典并没有"最后的元素"或者其他有关顺序的概念。若想一个接一个地移除并处理项,这个方法就非常有效。
a = {'name':'CSDN','age':18,'job':'程序员'}
r1 = a.popitem()
print(r1) # ('job', '程序员')
r2 = a.popitem()
print(r2) # ('age', 18)
r3 = a.popitem()
print(r3) # ('name', 'CSDN')
print(a) # {}
序列解包
序列解包可用于元组、列表、字典。序列解包可以让我们方便的对多个变量赋值
x,y,z=(20,30,10)
(a,b,c)=(9,8,10)
[m,n,p]=[10,20,30]
序列解包用于字典时,默认是对“键”进行操作; 如果需要对键值对操作,则需要使用items();如果需要对“值”进行操作,则需要使用values();
s = {'name':'CSDN','age':18,'job':'程序员'}
name,age,job=s
print(name) # name
name,age,job=s.items()
print(name) # ('name', 'gaoqi')
name,age,job=s.values()
print(name) # CSDN
字典核心底层原理(存储键值对的过程)
字典对象的核心时散列表。散列表是一个稀疏数组(总是有空白元素的数组),数组的每个单元叫做bucket。每个bucket有两部分:一个是键对象的引用,一个是值对象的引用。由于所有bucket结构和大小一致,我们可以通过偏移量俩读取指定bucket。
将一个键值对放进字典的底层过程
a = {}
a["name"]="竹筒饭"
假设字典a对象创建完后,数组长度为8:
把“name” = “竹筒饭”这个键值对放到字典对象a中,首先第一步需要计算键“name”的散列值。Python中通过hash()计算。
>>> bin(hash("name"))
'-0b1010111101001110110101100100101'
由于数组长度为8,我们可以拿计算出的散列值的最右边3位数字作为偏移量,即“101”,十进制是数字5。查看偏移量5对应的bucket是否为空。如果为空,则将键值对放进去,如果不为空,则依次取左边3位作为偏移量,即“100”,十进制是数字4。再查看偏移量为4的bucket是否为空。直到找到为空的buckrt将键值对放进去。
Python会根据散列表的拥挤层度扩容。扩容就是创造更大的数组,将原有内容拷贝到新数组中。
接近2/3时,数组就会扩容。
字典核心底层原理(查找键值对的过程)
和存储的底层流程算法一致,也是依次取散列值的不同位置的数字。假设数组长度为8,我们可以拿计算出的散列值的最右边3位数字作为偏移量,即101,十进制是数字5,查看对应的bucket是否为空。如果为空,则返回None。如果不为空,则将计算这个bucket的键对象的散列值,进行比较,如果相等则将对应的值对象负担会,如果不相等则再一次取其他几位数字,重新计算偏移量。依次取完后,仍然没有找到则返回None。
用法总结:
字典在内存中开销巨大,典型的空间换时间。
键查询速度很快
往字典里面添加新键值对可能导致扩容,导致散列表中键的次序变化。因此,不要在遍历字典的同时进行字典的修改
键必须可散列
数字、字符串、元组,都是可散列的
自定义对象需要支持下面三点:
支持hash()函数
支持通过__eq__()方法检测相等性
若a==b为真,则hash(a)==hash(b)也为真