01. 字典类型概述

1.1 字典概述

  • 字典是一个无序可变的KeyValue序列,存储的是有关联的数据。
  • 字典是用键来索引值的,其中键是唯一且不可变的。
    • 这个不可变不仅要求键的内容不可变,还要求键的数据类型是不可变的,如数字、字符串、元组、不可变集合可以作为键,列表、字典、集合等可变数据类型不可作为键。
    • 如果字典有相同的键,则后面的键对应的值会将前面的值覆盖。
  • 字典类型可以很好的展示数据所表达的含义,适合存储有映射关系的数据,如各个科目的成绩:

    1. score = {"Chinese": 97, "Math": 95, "Engligh": 88, "Science": 99}

    1.2 字典的定义

    1.2.1 字面量{}定义法

  • 使用字面量{}定义字典,格式:{Key1:Value1, Key2:Value2, ……, KeyN:ValueN}

    1. dic1 = {"Chinese": 97, "Math": 95, "Engligh": 88, "Science": 99}
    2. print(dic1) # {'Chinese': 97, 'Math': 95, 'Engligh': 88, 'Science': 99}

    1.2.2 dict()函数构建字典

  • 使用dict()函数,参数为一个包含n个(k, v)的二元组的列表或元组。 ```python dic2 = dict([(“Chinese”, 97), (“Math”, 95), (“Engligh”, 88), (“Science”, 99)]) # 参数为列表。 print(dic2) # {‘Chinese’: 97, ‘Math’: 95, ‘Engligh’: 88, ‘Science’: 99}

dic3 = dict(((“Chinese”, 97), (“Math”, 95), (“Engligh”, 88), (“Science”, 99))) # 参数为元组。 print(dic3) # {‘Chinese’: 97, ‘Math’: 95, ‘Engligh’: 88, ‘Science’: 99}

data = enumerate(“Hello”) # 生成带有多个(索引, 值)的序列。 dic4 = dict(data) print(dic4) # {0: ‘H’, 1: ‘e’, 2: ‘l’, 3: ‘l’, 4: ‘o’}

  1. - 使用dict()函数的可变形参,格式为:`dict(Key1=Value1, Key2=Value2, ……, KeyN=ValueN)`
  2. - 参数名Key会转换成字典的键,参数值Value会转换成键对应的值。
  3. ```python
  4. dic5 = dict(Chinese=97, Math=95, Engligh=88, Science=99)
  5. print(dic5) # {'Chinese': 97, 'Math': 95, 'Engligh': 88, 'Sicence': 99}

02. 字典的相关操作

2.1 字典的查询操作

2.1.1 键索引查询

  • 字典没有像列表、元组那样的下标索引,字典是通过键来索引数据的。
  • 键索引的格式:字典对象[键],但当键在字典中不存在时,会报错。

    1. score = {"Chinese": 97, "Math": 95, "Engligh": 88, "Science": 99}
    2. # 索引存在的键
    3. print(score["Chinese"]) # 97
    4. print(score["Math"]) # 95
    5. print(score["Engligh"]) # 88
    6. print(score["Science"]) # 99
    7. # 索引不存在的键
    8. print(score["Physics"]) # 报错:KeyError: 'Physics'
  • 键索引是没有先后顺序的,因此字典是没有切片操作的。

    2.1.2 get()查询键对应的值

  • 使用键索引获取值时,但当键在字典中不存在时,会报错。

  • 可以使用字典对象的get(“key”, defaultValue)方法,若Key不存在,则会返回默认值。

    1. print(score.get("Chinese", "Null")) # 97
    2. print(score.get("Physics", "Null")) # Null
    3. print(score.get("Biology", "Null")) # Null
  • 若没有指定defaultValue,且Key不存在,也不会报错。因为defaultValue存在缺省值None,None在Python表示空。

    1. print(score.get("Biology")) # None

    2.1.3 items()获取键值对/keys()获取所有键/values()获取所有值

  • 除了获取指定Key的Value外,字典对象还支持用item()函数获取所有KeyValue元组、用keys()函数获取所有key、用values()函数获取所有value。

    1. print(score.items()) # dict_items([('Chinese', 97), ('Math', 95), ('Engligh', 88), ('Science', 99)])
    2. print(score.keys()) # dict_keys(['Chinese', 'Math', 'Engligh', 'Science'])
    3. print(score.values()) # dict_values([97, 95, 88, 99])

    2.1.4 len()获取字典的长度

  • len(dict):用于获取字典dict的长度,其中一对键值对为长度1。

    1. print(len(score)) # 4

    2.2 字典的删除操作

    2.2.1 pop()删除指定键的值

  • dict.pop(key):删除并返回key对应的值。

    1. dic = {"k1": "v1", "k2": "v2", "k3": "v3", "k4": "v4", "k5": "v5"}
    2. delete = dic.pop("k3")
    3. print(dic, delete) # {'k1': 'v1', 'k2': 'v2', 'k4': 'v4', 'k5': 'v5'} v3

    2.2.2 popitem()随机删除一个键的值

  • dict.popitem():随机删除并返回被删掉的(key, value)。(一般都是删最后一个)

    1. delete = dic.popitem()
    2. print(dic, delete) # {'k1': 'v1', 'k2': 'v2', 'k4': 'v4'} ('k5', 'v5')

    2.2.3 clear()清空字典

  • dict.clear():清空字典。

    1. dic.clear()
    2. print(dic) # {}

    2.2.4 del dict[key]删除键/del dict删除字典

  • del dict[key]:用于删除字典dict中key对应的键值对。

  • del dict:clear()只是清空字典中的所有元素,这个字典还是存在的;而del则是将字典从内存空间中删除。

    1. deldic = {"k1": "v1", "k2": "v2"}
    2. del deldic["k1"]
    3. print(deldic) # {'k2': 'v2'}
    4. del deldic
    5. # print(deldic) # NameError: name 'deldic' is not defined

    2.3 字典的修改与运算

    2.3.1 dict[key]添加/修改元素

  • 字典可以使用dict[newKey] = value的方式添加元素,但当key已经存在,则会修改现有Key的值。

    • 注意:添加操作只能将新的键值对添加到字典的末尾。
    • 由于字典没有下标索引,因此字典没有在指定位置添加键值对的操作。 ```python dic = {} # 空字典

添加新的键值对

dic[“k1”] = “value1” dic[“k2”] = “value1” print(dic) # {‘k1’: ‘value1’, ‘k2’: ‘value1’}

修改已有键

dic[“k1”] = “new Value1” print(dic) # {‘k1’: ‘new Value1’, ‘k2’: ‘value1’}

  1. <a name="J8g68"></a>
  2. #### 2.3.2 setdefault()添加键值对
  3. - 字典还可以通过dict.setdefault(key, value)的方式添加键值对。
  4. ```python
  5. dic.setdefault("k3", "value3")
  6. print(dic) # {'k1': 'new Value1', 'k2': 'value1', 'k3': 'value3'}
  • 若key已经存在,则setdefault()不会做任何操作,即不会更新数据,也不会报错。

    1. dic.setdefault("k3", "new Value3")
    2. print(dic) # {'k1': 'new Value1', 'k2': 'value1', 'k3': 'value3'},k3的值并没有改变。

    2.3.3 update()更新字典

  • 所谓的更新字典,实际上就是用dic1.update(dic2)函数将dic2合并到dic1中。

  • 合并规则:如果dic2中的key在dic1中不存在,则对dic1进行扩展;如果dic2中的key在dic1中存在,则对dic1进行更新。

    1. dic1 = {"k1": "v1", "k2": "v2"}
    2. dic2 = {"k2": "value2", "k3": "v3"}
    3. dic1.update(dic2)
    4. print(dic1) # {'k1': 'v1', 'k2': 'value2', 'k3': 'v3'}
  • 并且只要update()函数的参数是可以被构造成字典的对象,update()函数也会自动进行构造。

    1. dic = {"k1": "v1", "k2": "v2"}
    2. data = [("k2", "value2"), ("k3", "v3")]
    3. dic.update(data)
    4. print(dic) # {'k1': 'v1', 'k2': 'value2', 'k3': 'v3'}

    2.3.4 成员运算(in、not in)

  • 字典不支持算数、比较等运算(但支持==!=),只支持成员运算(in、not in)。 ```python score1 = {“Chinese”: 97, “Math”: 95, “Engligh”: 88, “Science”: 99} score2 = {“Data Structure”: 83, “Math”: 65, “Engligh”: 91, “Data Analysis”: 77} score3 = {“Chinese”: 97, “Math”: 95, “Engligh”: 88, “Science”: 99} print(score1 == score2) # False print(score1 != score2) # True print(score1 == score3) # True,只有当键值对完全一样时,才为True,只要有一个Key或者一个Value不一样就为False。

不支持其他比较运算符

print(score1 > score2) # 报错,TypeError: ‘>’ not supported between instances of ‘dict’ and ‘dict’

  1. - 字典中的数据是被键标识的,成员运算符只能判断键是否存在,无法判断值是否存在。
  2. ```python
  3. score = {"Chinese": 97, "Math": 95, "Engligh": 88, "Science": 99}
  4. print("Chinese" in score) # True
  5. print("Physics" not in score) # True
  6. print(95 in score) # False,无法判断值

2.3.5 copy()复制字典

  • dic2 = dic1.copy()可以将字典dic1复制一份,并赋值给dic2,且dic1和dic2在内存中的地址不一样。

    1. dic1 = {"k1": "v1", "k2": "v2"}
    2. dic2 = dic1.copy()
    3. print(id(dic1), dic1) # 2551546355968 {'k1': 'v1', 'k2': 'v2'}
    4. print(id(dic2), dic2) # 2551546356160 {'k1': 'v1', 'k2': 'v2'}

    2.4 字典的遍历与数据过滤

    2.4.1 字典的遍历

  • 普通遍历:直接用for-in遍历字典只能获取到字典的键,无法获取到字典的值。 ```python score = {“Chinese”: 97, “Math”: 95, “Engligh”: 88, “Science”: 99} for i in score: print(i)

“”” 运行结果: Chinese Math Engligh Science “””

  1. - 遍历字典所有的键:
  2. ```python
  3. score = {"Chinese": 97, "Math": 95, "Engligh": 88, "Science": 99}
  4. for key in score.keys():
  5. print(key)
  6. """
  7. 运行结果:
  8. Chinese
  9. Math
  10. Engligh
  11. Science
  12. """
  • 遍历字典所有的值: ```python score = {“Chinese”: 97, “Math”: 95, “Engligh”: 88, “Science”: 99} for value in score.values(): print(value)

“”” 运行结果: 97 95 88 99 “””

  1. - 遍历字典所有的键值对:
  2. ```python
  3. score = {"Chinese": 97, "Math": 95, "Engligh": 88, "Science": 99}
  4. for item in score.items():
  5. print(item)
  6. """
  7. 运行结果:
  8. ('Chinese', 97)
  9. ('Math', 95)
  10. ('Engligh', 88)
  11. ('Science', 99)
  12. """
  13. # 解包操作
  14. score = {"Chinese": 97, "Math": 95, "Engligh": 88, "Science": 99}
  15. for key, value in score.items():
  16. print(key, value)

2.4.2 字典数据的过滤与筛选

  • 解决思路:
    • 定义一个空字典用于接收筛选出的数据。
    • 根据需求明确是根据键过滤,值过滤还是键值对过滤。
    • 根据过滤的元素,遍历字典。
    • 筛选出符合条件的元素。
  • 示例:筛选出成绩在90分以上的科目以及成绩。

    1. excellent_subjects = {}
    2. score = {"Chinese": 97, "Math": 95, "Engligh": 88, "Science": 75}
    3. for key, value in score.items():
    4. if value >= 90:
    5. excellent_subjects[key] = value
    6. print(excellent_subjects) # {'Chinese': 97, 'Math': 95}
  • 错误思路:遍历字典中的元素,当遇到成绩小于90的键值对时,删除该元素。

  • 错误原因:字典在迭代时长度不能被修改。

    1. score = {"Chinese": 97, "Math": 95, "Engligh": 88, "Science": 75}
    2. for i in score:
    3. if score[i] < 90:
    4. score.pop(i) # 报错,RuntimeError: dictionary changed size during iteration
    5. print(score)
  • 当然,在02. 列表的2.2.5中介绍过一种方法也适用于当前场景,即遍历新字典删除老字典。

    1. score = {"Chinese": 97, "Math": 95, "Engligh": 88, "Science": 75}
    2. tem_score = score.copy() # 复制一份得到新字典。
    3. for i in tem_score:
    4. if tem_score[i] < 90:
    5. score.pop(i)
    6. del tem_score # 计算完成后,临时字典可以删除掉
    7. print(score) # {'Chinese': 97, 'Math': 95}

    2.5 字典推导式

  • 与列表推导式一样,字典推导式也是定义列表的一种方式,代码简洁,可提升执行效率。

  • 基本格式:[键: 值 遍历操作[ 筛选操作]]
    • 注:筛选操作用于筛选遍历中的数据,不是必要的。
    • 遍历操作得到的一定是两个数据或者一对二维序列。
  • 示例:在一个字典中保存了股票的代码和价格,找出股价大于100元的股票并创建一个新的字典。

    • 股票字典如下:

      1. stocks = {
      2. 'AAPL': 191.88,
      3. 'G00G': 1186.96,
      4. 'IBM': 149.24,
      5. 'ORCL': 48.44,
      6. 'ACN ': 166.89,
      7. 'FB': 208.09,
      8. 'SYMC ': 21.29
      9. }
    • 使用普通操作实现:

      1. over_100 = {}
      2. for code, price in stocks.items():
      3. if price > 100:
      4. over_100[code] = price
      5. print(over_100)
    • 使用字典推导式实现:

      1. over_100 = {code: price for code, price in stocks.items() if price > 100}
      2. print(over_100)

      03. 字典的常见应用

      3.1 词频统计

      3.1.1 词频统计介绍

  • 词频统计就是指统计一段信息中,每个词出现的次数。

  • 如统计字符串s = "nice my name is hanmeimei"中每个字符出现的次数。
  • 在Python中,词频统计的结果一般以字典的形式存储,格式为:{字符1: 次数, 字符2: 次数, ……, 字符n: 次数}

    3.1.2 词频统计的实现思路

  • 定义一个空字典。

  • 遍历有序序列中每一个元素的内容,然后将元素及其对应的次数按照{元素: 次数}的格式存储字典中。
  • 字典在存储一个元素时,需要根据情况分类讨论:

    • 当前遍历到的元素字典中没有,则将这个元素作为新键添加到字典中,并赋初始值为1。
    • 当前遍历到的元素字典中已经存在,则更新这个键,值为原始值+1。

      3.1.3 词频统计的实现

  • 示例1:从键盘中输入一个字符串,要求对这个字符串进行词频统计。

    1. s = input("输入一段文本:")
    2. ch_count = {}
    3. for ch in s:
    4. if ch in ch_count:
    5. ch_count[ch] += 1
    6. else:
    7. ch_count[ch] = 1
    8. print(ch_count)
    • 运行结果:
      1. 输入一段文本:nice my name is hanmeimei
      2. {'n': 3, 'i': 4, 'c': 1, 'e': 4, ' ': 4, 'm': 4, 'y': 1, 'a': 2, 's': 1, 'h': 1}
  • 示例2:现有数组nums = [19, 27, 33, 19, 45, 33, 27, 51, 19, 65, 28, 33, 27],统计数组中每个数字出现的次数,并找出出现次数最多的数。 ```python nums = [19, 27, 33, 19, 45, 33, 27, 51, 19, 65, 28, 33, 27]

词频统计

num_count = {} for num in nums: if num in num_count: num_count[num] += 1 else: num_count[num] = 1 print(num_count) # {19: 3, 27: 3, 33: 3, 45: 1, 51: 1, 65: 1, 28: 1}

找出出现最多的数出现的次数。

max_count = max(num_count.values()) print(max_count) # 3

根据次数找到对应的数。

lot_num = [num for num, count in num_count.items() if count == max_count] print(lot_num) # [19, 27, 33]

  1. <a name="UYEj6"></a>
  2. #### 3.1.4 分类词频统计
  3. - 分类词频统计与普通词频统计最大的区别在于分类词频统计中一开始就知道所有的键,但是不知道具体的值,因此可以赋予初始值0。
  4. - 示例1:分别统计字符串`s = "HellO 123 PyThon3,HeLLo 456 BigData"`中大写字母、小写字母、数字以及其他符号出现的次数。
  5. ```python
  6. s = "HellO 123 PyThon3,HeLLo 456 BigData"
  7. word_count = {"upper": 0, "lower": 0, "digit": 0, "other": 0}
  8. for ch in s:
  9. if "a" <= ch <= "z":
  10. word_count["lower"] += 1
  11. elif "A" <= ch <= "Z":
  12. word_count["upper"] += 1
  13. elif "0" <= ch <= "9":
  14. word_count["digit"] += 1
  15. else:
  16. word_count["other"] += 1
  17. print(word_count) # {'upper': 9, 'lower': 14, 'digit': 0, 'other': 3}
  • 示例2:现有数组nums = [19, 27, 81, 44, 53, 26, 91, 48],对奇数、偶数先分类,再求和。 ```python nums = [19, 27, 81, 44, 53, 26, 91, 48]

分类

num_count = {“odd”: [], “even”: []} for num in nums: if num % 2 == 0: num_count[“even”].append(num) else: num_count[“odd”].append(num) print(num_count) # {‘odd’: [19, 27, 81, 53, 91], ‘even’: [44, 26, 48]}

求和

num_sum = {“odd”: 0, “even”: 0} num_sum[“odd”] = sum(num_count[“odd”]) num_sum[“even”] = sum(num_count[“even”]) print(num_sum) # {‘odd’: 271, ‘even’: 118}

  1. <a name="x9luT"></a>
  2. ### 3.2 字典与列表结合
  3. <a name="cK3tL"></a>
  4. #### 3.2.1 字典与列表结合应用场景举例
  5. - 字典存储着有关联信息的数据,如存储一个学生的信息`student = {student_id: "2021031001", "name": "Xiao Ming", "age": 19, "gender": "male", "score": 673}`
  6. - 但是一个班级中,肯定会有多个学生,也就是说会有多个字典。此时,要描述全班同学的信息,就可以用一个列表,这个列表中存储的就是每个存有学生信息的字典。
  7. - 为什么用列表而不用元组:选择元组还是选择列表需要根据其特性,结合实际场景选择。
  8. - 对于一个班级而言,可能会出现班级中的学生退学、转学、休学或者加入插班生的情况,虽然这种情况并不会经常发生,但这也代表着不能用一个不可变的容器存储学生信息,因此此场景选择列表比较合适。
  9. - 示例:采用字典与列表结合的形式存储4位学生的信息。
  10. ```python
  11. class_1 = [
  12. {"student_id": "2021031001", "name": "Dosbo", "age": 17, "gender": "male", "score": 673},
  13. {"student_id": "2021031002", "name": "Adam", "age": 21, "gender": "male", "score": 689},
  14. {"student_id": "2021031003", "name": "Jenna", "age": 23, "gender": "female", "score": 573},
  15. {"student_id": "2021031004", "name": "Katrina", "age": 16, "gender": "female", "score": 449}
  16. ]
  • 这个列表中的所有字典可以通过列表名[索引]的形式定位,字典中的内容可以通过列表名[索引][Key]的形式定位。

    1. print(class_1[1]) # {'student_id': '2021031002', 'name': 'Adam', 'age': 21, 'gender': 'male', 'score': 689}
    2. print(class_1[2]["name"]) # Jenna

    3.2.2 字典与列表结合的实际应用

  • 示例1:找出class_1中所有未成年的学生。

    1. class_1 = [
    2. {"student_id": "2021031001", "name": "Dosbo", "age": 17, "gender": "male", "score": 673},
    3. {"student_id": "2021031002", "name": "Adam", "age": 21, "gender": "male", "score": 689},
    4. {"student_id": "2021031003", "name": "Jenna", "age": 23, "gender": "female", "score": 573},
    5. {"student_id": "2021031004", "name": "Katrina", "age": 16, "gender": "female", "score": 449}
    6. ]
    7. juveniles = []
    8. for stu in class_1:
    9. if stu["age"] < 18:
    10. juveniles.append(stu)
    11. print(juveniles) # [{'student_id': '2021031001', 'name': 'Dosbo', 'age': 17, 'gender': 'male', 'score': 673}, {'student_id': '2021031004', 'name': 'Katrina', 'age': 16, 'gender': 'female', 'score': 449}]
  • 示例2:找出class_1中所有分数高于650的男生。

    1. class_1 = [
    2. {"student_id": "2021031001", "name": "Dosbo", "age": 17, "gender": "male", "score": 673},
    3. {"student_id": "2021031002", "name": "Adam", "age": 21, "gender": "male", "score": 689},
    4. {"student_id": "2021031003", "name": "Jenna", "age": 23, "gender": "female", "score": 573},
    5. {"student_id": "2021031004", "name": "Katrina", "age": 16, "gender": "female", "score": 449}
    6. ]
    7. excellent_boy = []
    8. for stu in class_1:
    9. if stu["gender"] == "male" and stu["score"] >= 650:
    10. excellent_boy.append(stu)
    11. print(excellent_boy) # [{'student_id': '2021031001', 'name': 'Dosbo', 'age': 17, 'gender': 'male', 'score': 673}, {'student_id': '2021031002', 'name': 'Adam', 'age': 21, 'gender': 'male', 'score': 689}]