【Python】Python中强大的数据结构和字典升级版
题目要求:
| No. | Standard input | Standard output |
|---|---|---|
| 1 | 5 Ivanov 5 Petrov 4 Ivanov 3 Sidorov 5 Petrov 5 |
Ivanov 4.00 Petrov 4.50 Sidorov 5.00 |
| 2 | 3 Ivanov 5 Ivanov 5 Petrov 3 |
Ivanov 5.00 Petrov 3.00 |
这是我写的代码:
1 | n = int(input()) |
最后通过下标访问列表很C,引入的新变量
可以直接遍历排序好的列表进行替代:
1 | # 直接遍历排序后的名字列表 |
另外,获取列表元素的个数可以用len函数
1 | child = len(name_list) # 一句话搞定 |
针对这种需要初始化字典键值的情况,Python 标准库里还有一个叫 collections.defaultdict 的神器,可以把中间那个 if...else... 判断省掉
什么是
defaultdict?
defaultdict是 Python 内置字典(dict)的一个升级版。它的超能力是:当你试图访问或修改一个不存在的键时,它不会报错,而是会自动帮你创建一个默认值。如果你告诉它值的类型是
int,它就会把默认值设为0;如果是list,默认值就是空列表[]。看看代码对比
以前的写法(手动检查并初始化):
1
2
3
4
5
6 if name not in grade_sum:
grade_sum[name] = grade
grade_count[name] = 1
else:
grade_sum[name] += grade
grade_count[name] += 1**使用
defaultdict后的写法(直接累加,无脑操作):
1
2
3 # 因为不存在时会自动初始化为 0,所以直接加就行!
grade_sum[name] += grade
grade_count[name] += 1
终极进化版代码
结合
defaultdict,并且直接对字典的键进行排序(连额外的name_list列表都不需要建了),代码可以浓缩成这样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 from collections import defaultdict
n = int(input())
# 告诉 defaultdict,字典里的值默认都是整数 (0)
grade_sum = defaultdict(int)
grade_count = defaultdict(int)
for _ in range(n):
name, grade = input().split()
grade = int(grade)
# 彻底告别 if...else,哪怕是第一次遇到这个名字,
# defaultdict 也会悄悄把它初始化为 0,然后顺利加上去
grade_sum[name] += grade
grade_count[name] += 1
# 直接获取字典所有的键(名字),排序后遍历
for name in sorted(grade_sum.keys()):
avg = grade_sum[name] / grade_count[name]
print(f"{name} {avg:.2f}")为什么推荐这么写?
- 逻辑更专注:不需要再去关心“这个变量初始化了没有”,大脑可以完全聚焦在“我要计算总和与次数”这个核心逻辑上。
- 结构更精简:从维护三个容器(一个列表、两个字典),变成了只维护两个字典,减少了数据冗余。
其实这里可以用列表作为字典的值,巧妙地利用了Python强大的数据结构
1 | n = int(input()) |
顺带讲一下这里的格式化输出:
1 | print(f"{name} {avg_grade:.2f}") |
- 开头的字母
f
- 只有在字符串的引号前面加上
f(或F),这个字符串才会拥有魔法。如果没有f,Python 就会把它当成普通文本,原样输出{name}这几个字符。
- 第一处填空
{name}
- 这里的要求是“原样放入变量”。Python 看到
{name},就会去内存里找变量name的值(比如 “Ivanov”),直接填在这个位置。
- 第二处填空
{avg_grade:.2f}
这里分成了两部分,中间用冒号 : 隔开:
- 冒号左边 (
avg_grade):你要放入的变量。 - 冒号右边 (
.2f):你要对这个变量执行的格式化规则。
那这一题最Pythonic的写法长啥样?
抛开 C 语言手动管理数据的包袱,在 Python 中处理这类“分组收集再统计”的问题,最“标准”且最能体现 Python 优雅风格(Pythonic)的写法,是使用 defaultdict(list)。
它的核心思路是:把每一个学生的名字作为键,把他们的所有成绩放进一个列表里。最后利用内置函数直接求和、求长度。
这是这道题的标准答案:
1 | from collections import defaultdict |
1. defaultdict 的本体是什么?
只要你写下 defaultdict(...),你就是在创建一个字典。它和普通字典 {} 的结构一样,都是由“键-值对”(Key-Value)组成的。
2. 括号里的参数(int 或 list)决定了什么?
括号里的值,决定的不是这个容器本身是什么,而是决定了:当这个字典遇到一个不存在的键时,它自动补上的“默认值”是什么。
defaultdict(int)- 动作:创建一个字典。当访问不存在的键时,自动调用
int(),默认补上一个整数0。 - 长相:字典里的“值”都是数字,例如
{'Ivanov': 5, 'Petrov': 4}。 - 最佳用途:计数、累加。
- 动作:创建一个字典。当访问不存在的键时,自动调用
defaultdict(list)- 动作:创建一个字典。当访问不存在的键时,自动调用
list(),默认补上一个空列表[]。 - 长相:字典里的“值”都是列表,例如
{'Ivanov': [5, 3], 'Petrov': [4]}。 - 最佳用途:分组、收集数据。(像标准答案里把同一个人的所有成绩装进同一个列表里)。
- 动作:创建一个字典。当访问不存在的键时,自动调用
3. 为什么这是标准答案?
- 逻辑极简:没有中间的计数器变量,没有初始化的判断语句。你读这段代码,就像读自然的英语逻辑一样顺畅:“提取名字和成绩、把成绩追加给这个人、排序并打印平均分”。
- 充分利用内置函数:在 C 语言里,为了计算数组的总和,需要写一个循环遍历累加;在 Python 里,直接把列表丢给
sum(scores),用len(scores)获取个数,底层由 C 语言实现的内置函数运行速度远快于你手写的 Python 循环。 - 数据结构清晰:在这个版本中,字典长这样:
{'Ivanov': [5, 3, 5], 'Petrov': [4, 5], ...}。数据结构直接反映了客观世界的逻辑(一个人对应多个成绩),非常直观。



