在Python中有两种最常用的基础数据结构,一个是你已耳熟能详的列表,另外一个是你即将耳熟能详的……字典。这字典不是新华字典、不是康熙字典、不是有道字典、不是英汉字典,不是我的生命里就没有不行这俩字的字典,而是又被称为散列表、哈希表、关联数组和映射的dictionary……打个响指,认真脸,在Python中,字典是有一系列键值对组成的集合。就像我们上期学习过的集合一样,也具有可迭代性、可变性、唯一性和无序性的特点,但和集合所不同的是,它储存的不是单一元素,而是键值对[key-value pairs]。创建字典通常使用花括号{}或者dict()构造函数,以下代码均可以创建一个空字典。摊手,还记得上期教程中那句花括号无法创建空集合吧?原因就在于👆
以下代码使用花括号{}创建的字典包含了1个键值对,其中键key为'姓名',值value为'看见星光'。以下代码使用dict()函数创建的字典包含了2个键值对。d = dict(
姓名 = '看见星光',
性别 = '男'
)
观察两段代码的不同之处,花括号的键值对语法格式为key:value,其中key为常量字符串时,需要使用引号包裹起来,而dict函数的键值对语法格式为key=value,key作为变量使用,不需要也不能使用引号包裹起来。在实际应用中,经常需要将列表等可迭代对象直接转换为字典。当一个列表是n行2列的结构时,可以直接使用dict()函数转换为字典。它默认第1列为键key,第2列为值value。以下代码将列表r转换为字典:{'姓名': '看见星光', '性别': '男'}
r = [['姓名','看见星光'],['性别','男']]
d = dict(r)
print(d)
如果有两个一维列表,其中一个为键key,另外一个为值value,可以先使用zip()函数将它们组合成键值对结构的迭代器,再使用dict()函数转换。arkey = ['姓名','看见星光']
arValue = ['性别','男']
d = dict(zip(arkey,arValue))
使用字典推导式可以快速从可迭代对象中创建字典,它非常类似列表推导式,只是生成的结果不是列表,而是字典。语法格式如下:{key_expression: value_expression for item in iterable}
语法中key_expression是生成键的表达式,value_expression是生成值的表达式,两者之间用冒号相连。例如,以下代码有一个n行3列的列表r,需要将第1列作为key,最后一列作为value,生成一个字典:{'看见星光': 16, '王小睿': 15, '星辰': -16},参考代码如下:r = [
['看见星光','语文',16],
['王小睿','语文',15],
['星辰','语文',-16],
]
d = {s[0]:s[-1] for s in r}
print(d)
再例如,有一个混合字符串构成的列表r,需要将其每个元素按短横杠拆分,将姓名作为key,性别转为value,生成字典:{'看见星光': '男', '王小睿': '女', '源哥': '女'}——这是本期的第一个练习题——个别学员朋友应该高标准严要求使用一行代码完成它,典型如星辰。r = [
'看见星光-男-语文-16',
'王小睿-女-数学-15',
'源哥-女-体育-16'
]
需要注意的是字典的键key具有唯一性,只能使用不可变的数据类型,例如数字、字符串和元组等。值value则没有数据类型的限制,可以是不可变的数据类型,也可以是可变数据类型,例如列表、集合、字典等。
以下代码将key设置为列表,违反了键key的约束规则,会抛出代码异常:unhashable type: 'list'
以下代码中key'性别'出现了2次,违反了字典的key具有唯一性的原则,字典会使用后出现的键值对替代前面的键值对,最后返回字典:{'姓名': '星辰', '性别': '女'}
r = [
['姓名','星辰'],
['性别','男'],
['性别','女']
]
d = dict(r)
print(d)
字典创建完成后,可以添加新的键值对,常用操作有以下几种方式。往字典里添加新的元素,最简单最常用的方法是直接赋值。如果键已经存在,值会被更新;如果键不存在,会新增一个键值对。d = {}
d['姓名'] = '看见星光'
print(d) # {'姓名': '看见星光'}
d['姓名'] ='王小睿'
print(d) # {'姓名': '王小睿'}
以上代码中,先使用花括号创建了一个空字典。第2行代码新增一个键值对,key为姓名,value为看见星光。此时字典中不存在该key,直接新增该键值对,因此第3行代码打印结果为:{'姓名': '看见星光'}第4行代码又新增一个键值对,key为姓名,value为王小睿。此时字典中存在相同的key姓名,于是更新对应的值。最终第5行代码打印字典的内容为:{'姓名': '王小睿'}。如果需要往字典中批量添加多个键值对,可以使用update()方法。它接受一个字典或一个包含键值对结构的可迭代对象作为参数。以下代码将一个n行两列结构的二维数组添加到字典中,其中第1列默认为key,第2列默认为value:d = {'班级':'一班'}
r = [
['姓名','看见星光'],
['性别','男']
]
d.update(r)
print(d)
以下代码将一个字典合并到另一个字典中,相同的key会被后者更新,最后打印字典d1的内容为:{'班级': '二班', '姓名': '看见星光'}
d1 = {'班级':'一班'}
d2 = {'姓名':'看见星光','班级':'二班'}
d1.update(d2)
print(d1)
3 丨 访问键值对
在其它系列教程中我们讲过,数据处理的过程,总结起来就是3个步骤,读取数据、计算数据、输出数据。字典计算数据的过程也不例外,将键值对添加到字典中,属于读取数据,数据计算完成后,还需要将其从字典中输出,也就是访问键值对。键值对添加到字典中之后,可以通过键key快速访问对应的值value。以下代码中,第2行代码访问键'姓名'对应的value,结果为'看见星光'。d = {'姓名':'看见星光','班级':'二班'}
d['姓名'] #'看见星光'
当字典中不存在指定的key时,代码会抛出异常:KeyError。d = {'姓名':'看见星光','班级':'二班'}
d['看见星光']
以上代码中第2行代码访问字典中不存在的键'看见星光',抛出异常结果如下图所示。为了避免抛出异常,可以先使用成员运算符in判断字典中是否存在指定key,再访问key对应的value。d = {'姓名':'看见星光','班级':'二班'}
key = '看见星光'
if key in d:
print(d[key])
else:
print('没有这个人知道伐?')
以上代码中第3行代码判断字典中是否存在指定key,条件成立则打印对应的value,否则打印字符串:没有这个人知道伐?除了使用以上方法之外,也可以使用get()方法获取指定键的值。如果键不存在,该方法会返回第2参数指定的默认值,如果第2参数省略,则返回默认值 None。
d = {'姓名':'看见星光','班级':'二班'}
print(d.get('看见星光'))
print(d.get('看见星光','没有这个人知道伐'))
print(d.get('姓名'))
以上代码中第2~3行代码使用get()方法获取指定key对应的value,此时,字典中都不存在key:看见星光。其中第2行代码的get()方法没有指定第2参数,于是打印结果为None,第3行代码指定了第2参数,打印结果为:没有这个人知道伐?第4行代码由于字典中存在指定key:姓名,则正常打印对应的value:看见星光。当访问的key在字典中可能不存在也可能存在,如果存在,则返回对应的value,如果不存在,你却需要将该key直接写入字典中,那么可以使用setdefault()方法。setdefault() 方法获取指定键的值,如果键存在,则返回其值,如果键不存在,则插入该键并设置默认值。
d = {'看见星光':'男'}
print(d.setdefault('看见星光', '女'))
print(d.setdefault('王小睿', '女'))
print(d)
第2行代码使用setdefault() 方法获取key为'看见星光'的值。此时字典中已经存在该key,则不更新其值,并返回对应的value:男。第3行代码使用setdefault() 方法获取key为'王小睿'的值。此时字典中不存在该key,则将键值对'王小睿':'女'写入字典,并返回key王小睿对应的值:女。
第4行代码打印字典,返回内容:{'看见星光': '男', '王小睿': '女'}
……打个响指,猜一猜,以下代码返回的结果是什么?为什么?
d = {'姓名':'看见星光','班级':'二班'}
d[0]
字典这种通过键可以快速访问值的机制,非常适合于数据关联查询。
本期先举个简单的小栗子,更多数据关联查询的案例我们下期教程再详细讲解。在本期案例工作簿中,有一个名称为'示例1'的工作表,内容如下图所示,A列是姓名,D列是成绩,现在需要查询F列人名对应的成绩。
先创建一个代码单元格,使用以下代码打开目标工作簿:import xlwings as xw
app = xw.App(visible=True,add_book=False)
path = r'工作簿的全路径'
wb = app.books.open(path)
sht = wb.sheets['示例1'] # 选择工作表
aData = sht.range('a2:d2').expand('down').value # 读取数据源a~d列
aFind = sht.range('f2').expand('down').value # 读取需要查询的人名列
d = {r[0]:r[3] for r in aData} # 构造字典,姓名为key,成绩为value
aRes = [[d.get(key,'查无此人')] for key in aFind] #使用字典查询结果
sht.range('g2').value = aRes # 写入结果
代码中第1~3行代码分别将数据源和需要查询的人名写入列表aData和aFind。
第4行代码使用字典推导式,遍历列表aData,将第1列的姓名作为key,第4列的成绩作为value。第5行代码使用列表推导式,将姓名作为key查询对应的value成绩,如果字典中不存在指定人名,则返回字符串'查无此人'。
最后创建一个代码单元格,保存和关闭目标工作簿,退出创建的excel程序。
wb.save() # 保存
wb.close() # 关闭
app.quit() # 退出
这里留一道练习题(本期第2题),查询F列姓名对应的性别和成绩。以上介绍的方法都是访问单个的键值对,字典也提供了直接返回键集、值集和键值集的方法。keys()方法可以返回字典中所有键的视图。例如,以下代码返回结果:d = {'姓名':'看见星光','班级':'二班'}
d.keys()
values()方法可以返回字典中所有值的视图。例如,以下代码返回结果:dict_values(['看见星光', '二班'])d = {'姓名':'看见星光','班级':'二班'}
d.values()
items()方法可以返回字典中所有键值对的视图。例如,以下代码返回结果:dict_items([('姓名', '看见星光'), ('班级', '二班')])d = {'姓名':'看见星光','班级':'二班'}
d.items()
字典具有可迭代性,通过keys()/values()/itemes()等方法,可以快速迭代字典中的元素。假设有一个字典,如下所示,keys是姓名,values是考试不及格的次数,现在需要筛选出不及格次数大于3次的,写入二维列表aRes。
d = {
'看见星光':4,
'源哥':5,
'王小睿':2,
'宗姐':2,
'掌门':3,
}
aRes = []
for key in d.keys():
if d[key]>3 :
aRes.append([key,d[key]])
aRes
第2行代码使用keys()方法遍历字典的键集,第3行代码访问key对应的value,如果value大于3,则第4行代码将key和value写入列表aRes。aRes = [[key,item] for key in d.keys() if (item:= d[key])>3]
列表推导式的if子句执行筛选作用,其中使用了海象运算符,将d[key],也就是key对应的value,赋值变量item。
aRes = [[key,value] for key,value in d.items() if value>3]
代码使用了items()方法直接获取字典的键值对视图。摊手,以上代码各有优劣,都是你需要了解的,至于更详细的应用场景,后面两章字典应用经典案例中再见了。
字典具有可变性,可以原地修改字典中的元素,而不会创建一个新字典。在上面的案例中,我们介绍了如何往字典中添加键值对,这里再介绍一下如何修改或删除键值对。
当往字典中添加键值对时,如果键已经存在,值会被更新;如果键不存在,会新增一个键值对——利用这个特性,可以修改指定key对应的valule。d = {}
d['姓名'] = '看见星光'
print(d)
d['姓名'] ='王小睿'
print(d)
以上代码中,先使用花括号创建了一个空字典。第2行代码新增一个键值对,key为姓名,value为看见星光。此时字典中不存在该key,直接新增该键值对。第4行代码又新增一个键值对,key为姓名,value为王小睿。此时字典中存在相同的key姓名,于是更新对应的值。最终第5行代码打印字典的内容为:{'姓名': '王小睿'}。使用clear()方法可以删除字典中所有的键值对,将其杀得一干二净,杀完之后字典还在,是一个空字典。d = {'看见星光':'男'}
d.clear()
print(d)
如果需要删除指定键值对,使用del语句是最直接的方法,但如果键不存在,它会抛出异常:KeyError
以下代码中第6行代码使用del 语句删除字典中指定key为'星辰'的键值对,第7行代码打印字典,结果显示'星辰'被成功干掉了。
d = {
'看见星光':'男人',
'星辰':'娃娃',
'随风小妞':'女人'
}
del d['星辰']
print(d)
# 打印结果为 {'看见星光': '男人', '随风小妞': '女人'}
需要注意的是,del是语句,不是函数,括号不是必须的。表达式为:del d['星辰']。当然,你也可以写成del (d['星辰']),只是看起来有些多此一举,这里的括号()不是函数,而是分组,表示d['星辰']是一个整体。如果键不存在,del语句会抛出异常:KeyError。如果此时你不希望抛出异常,可以先使用成员运算符in判断字典中是否存在指定key,又或者使用pop()方法。pop() 方法用于移除并返回指定键的值。如果键不存在,它可以提供一个默认值,如果未提供默认值,则会抛出异常:KeyError。d = {
'看见星光':'男人',
'星辰':'美男子',
'随风小妞':'女人'
}
print(d.pop('星辰'))
print(d.pop('星辰','人已经不在了'))
以上代码中,第6行代码使用pop()方法移除key'星辰',并返回对应的value,打印结果为:'美男子'。第7行代码再次使用pop()方法移除key'星辰',尝试返回对应的value。但此时字典中已经不存在该key了,于是返回pop()指定的默认值'人已经不在了'。除了del和pop()方法之外,使用popitem() 方法可以随机移除并返回一个键值对。如果字典为空,会抛出 KeyError 异常——不常用,了解一下就好。和上一章教程我们所学习的集合和再之前学习的列表都不同,字典并没有提供Remove()的方法。另外,你还需要注意区分字典和集合两者中pop()方法的不同。
一口气写到这儿,我倦了,倦的像一朵被风吹散的野花那就挥挥手,下期再见吧。嘿~!更多Python系统教程,欢迎点击文末「阅读原文」加入我的Python知识星球。最后再留一个简单的练手题(本期第3题),在本期案例工作簿中,有一个名称为'示例2'的工作表,内容如下图所示,A~B两列存在重复的记录,需要删除重复项,模拟结果如D~E列所示。……
……
🔽点击阅读原文
从0到1系统学习Python