Skip to content

第8周:第五次上机课 ​

详细讲义 · py 作业 6 · OJ 题集

一、二维列表:列表里还可以放列表 ​

对应 04 · 矩阵换行求边缘和。

1. 先找行,再找这一行中的元素 ​

python
matrix = [[2, 4, 6], [1, 3, 5]]
print(matrix[0])     # [2, 4, 6],第 1 行
print(matrix[0][1])  # 4,第 1 行第 2 个元素
print(len(matrix))  # 2,行数
print(len(matrix[0]))  # 3,列数

matrix[i][j] 可以分成两步理解:matrix[i] 取出一整行,再用 [j] 取出这一行中的元素。下标从 0 开始。

2. 修改一个元素和交换两行 ​

python
matrix[0][1] = 9
matrix[0], matrix[1] = matrix[1], matrix[0]
print(matrix)  # [[1, 3, 5], [2, 9, 6]]

一行本身也是一个列表,可以像普通变量一样交换。a, b = b, a 会先取得右侧的两个值,再分别赋给左侧。

3. 用两层循环访问位置 ​

python
for i in range(len(matrix)):
    for j in range(len(matrix[i])):
        print(i, j, matrix[i][j])

外层决定走到哪一行,内层走过这一行的各列。

做题前想一想: 第 1 行对应哪个下标?最后一行呢?一个位置在边缘,可以通过它的行、列下标判断吗?

二、列表的赋值与复制 ​

对应 05 · 图像模糊处理。

1. 两个变量可能指向同一个列表 ​

python
a = [10, 20, 30]
b = a
b[0] = 99
print(a)  # [99, 20, 30]

b = a 没有新建列表。通过 b 修改元素,a 看到的内容也会变。

python
a = [10, 20, 30]
b = a[:]
b[0] = 99
print(a)  # [10, 20, 30]

对于这个整数列表,切片 a[:] 生成一个独立的新列表。

2. 二维列表要考虑里面的每一行 ​

python
image = [[10, 20], [30, 40]]
result = []
for row in image:
    result.append(row[:])

result[0][0] = 99
print(image[0][0])  # 10

这里逐行复制。仅写 image[:] 只新建外面的列表,里面的行仍然共用。

3. 用下标描述相邻位置 ​

假设当前位置是 (i, j),上一行是 i-1,下一行是 i+1,左一列是 j-1,右一列是 j+1。先在纸上画一个小方格,标出这些位置。

做题前想一想: 最外侧的点都有这四个邻居吗?题面要求使用“原灰度值”,计算新值时应该从哪个列表读取?

三、排序:先整理数据,再观察关系 ​

对应 07 · 小鹿的工作和休息时间。

1. 用两个数表示一个时间段 ​

python
period = (2, 6)
start, end = period
print(end - start)  # 4

元组可以把相关的数据放在一起;解包把它们分别交给两个变量。时间段长度是终点减起点。

2. 让一组时间段按开始时间排列 ​

python
periods = [(8, 10), (2, 6), (5, 7)]
ordered = sorted(periods, key=lambda item: item[0])
print(ordered)  # [(2, 6), (5, 7), (8, 10)]

sorted 返回排序后的新列表;key 告诉它“按什么比较”。这里每个 item 是一个时间段,item[0] 是开始时间。

lambda item: item[0] 相当于下面这个只做一件事的小函数:

python
def get_start(item):
    return item[0]

ordered = sorted(periods, key=get_start)

3. 在时间轴上看两个区间 ​

先画 [2,6],再分别画 [5,8]、[6,8]、[7,9],观察重叠、相接和有空隙的区别。判断关系时,需要比较哪些端点?

做题前想一想: 输入的时间段是乱序的,先排序有什么帮助?两段工作能连续进行,需要满足什么条件?

四、字典:用名字找到对应的数据 ​

对应 09 · 单词出现频率统计。

1. 列表用下标,字典用键 ​

python
counts = {"apple": 2, "pear": 1}
print(counts["apple"])          # 2
print(counts.get("banana", 0))  # 0

字典保存“键 → 值”的对应关系。get(key, 0) 表示:存在这个键就取出它的值,否则返回 0;这一步本身不会添加新键。

2. 从空字典开始计数 ​

python
counts = {}
for fruit in ["apple", "pear", "apple"]:
    counts[fruit] = counts.get(fruit, 0) + 1
print(counts)  # {'apple': 2, 'pear': 1}

每次先取旧值,再加 1,最后存回字典。第一次看到 apple,从 0 加到 1;再次看到它,从 1 加到 2。

3. 同时取出键和值 ​

python
for name, count in counts.items():
    print(name, count)

items() 可以遍历每一对键和值。若要排序,可以把这些二元组交给 sorted;排序依据由 key 指定。

做题前想一想: 统计单词时,什么作为键、什么作为值?如果两个词次数相同,题目希望按什么顺序输出?

五、字典累加与函数:把一条记录变成一个数 ​

对应 10 · 网站网页访问统计。

1. 字典里的值不一定每次加 1 ​

python
totals = {}
for name, amount in [("apple", 3), ("pear", 2), ("apple", 4)]:
    totals[name] = totals.get(name, 0) + amount
print(totals)  # {'apple': 7, 'pear': 2}

计数是每次加 1,求总量是每次加当前记录的数值。两者都要保留之前的累计结果。

2. 拆开字符串,再统一单位 ​

python
text = "01:02:03"
hour, minute, second = text.split(":")
print(hour, minute, second)  # 01 02 03

拆出来的仍是字符串,要先转成整数才能计算。比较或相减之前,把时、分、秒统一为秒:一小时 3600 秒,一分钟 60 秒。

如果多处都需要相同的转换,可以把它写成函数,用参数接收时间字符串,用 return 返回秒数。

做题前想一想: 一条访问记录能算出什么?同一网页出现多次时,字典里应该保存最后一次的时长,还是累计时长?

六、字典中的值也可以是列表 ​

对应 11 · 单词打分(提高版)。

1. 把同一类记录放在一起 ​

python
scores = {}
for subject, score in [("math", 80), ("english", 90), ("math", 100)]:
    if subject not in scores:
        scores[subject] = []
    scores[subject].append(score)

print(scores)  # {'math': [80, 100], 'english': [90]}

第一次遇到某个科目,先给它建一个空列表;之后把这个科目的分数追加进去。scores[subject] 取出来的是列表,所以可以继续调用 append()。

2. 一组数的总和、个数和平均值 ​

python
values = scores["math"]
print(sum(values))              # 180
print(len(values))              # 2
print(sum(values) / len(values))  # 90.0

平均值由总和与个数共同决定。也可以不保存全部分数,只记录总分和次数;选择怎样存数据,要看后面还需要用哪些信息。

做题前想一想: 如果最后既要输出单词,又要输出给它评分的人,只保存分数够吗?哪些信息需要在读取时一起留下?

课程资料由授课教师、助教与同学共同积累。