Python程序设计 期末备考教程(第4-7章)

第4章 复合数据类型

4.1 列表(list)

列表是Python中最常用的可变序列,用方括号 [] 表示,元素之间用逗号分隔。

创建列表

python 复制代码
# 空列表 []
empty_list = []

# 创建列表 [...]
numbers = [1, 2, 3, 4, 5]

# 创建列表 [...]
mixed = [1, "hello", 3.14, True]

# 创建列表 [...]
matrix = [[1, 2], [3, 4], [5, 6]]

列表的索引和切片

python 复制代码
# 创建列表 [...]
fruits = ["苹果", "香蕉", "橘子", "西瓜", "葡萄"]

print(fruits[0])   # 苹果
print(fruits[2])   # 橘子

print(fruits[-1])  # 葡萄
print(fruits[-2])  # 西瓜

print(fruits[1:4])     # ["香蕉", "橘子", "西瓜"](包含start,不包含stop)
print(fruits[:3])      # ["苹果", "香蕉", "橘子"]
print(fruits[2:])      # ["橘子", "西瓜", "葡萄"]
print(fruits[::2])     # ["苹果", "橘子", "葡萄"](步长为2)
print(fruits[::-1])    # ["葡萄", "西瓜", "橘子", "香蕉", "苹果"](反转)

常用列表方法

方法 功能 示例
append(x) 在末尾添加元素 fruits.append("芒果")
insert(i, x) 在指定位置插入元素 fruits.insert(1, "草莓")
remove(x) 删除第一个值为x的元素 fruits.remove("香蕉")
pop(i) 删除并返回指定位置的元素(默认最后一个) fruits.pop()
index(x) 返回第一个值为x的元素的索引 fruits.index("橘子")
count(x) 统计x出现的次数 fruits.count("苹果")
sort() 原地排序(升序) numbers.sort()
reverse() 反转列表 fruits.reverse()
len() 返回列表长度(内置函数) len(fruits)
python 复制代码
# 创建列表 [...]
numbers = [3, 1, 4, 1, 5, 9, 2]
numbers.append(6)       # [3, 1, 4, 1, 5, 9, 2, 6]
numbers.sort()          # [1, 1, 2, 3, 4, 5, 6, 9]
numbers.pop()           # 移除并返回9,列表变为[1, 1, 2, 3, 4, 5, 6]
print(len(numbers))     # 7

4.1.1 列表拷贝:赋值 vs copy() vs deepcopy()

列表拷贝是一个容易踩坑的知识点,务必分清以下三种操作的区别:

1. 直接赋值 = ------ 引用传递(共享同一对象)

python 复制代码
a = [1, 2, 3]
b = a           # b 和 a 指向同一个列表对象
b[0] = 999
print(a)        # [999, 2, 3]  ← a 也变了!
print(b)        # [999, 2, 3]
print(a is b)   # True(a 和 b 是同一个对象)

2. copy() ------ 浅拷贝(只复制第一层)

python 复制代码
a = [1, 2, 3]
b = a.copy()    # b 是 a 的浅拷贝,是一个新列表
b[0] = 999
print(a)        # [1, 2, 3]    ← a 不变
print(b)        # [999, 2, 3]
print(a is b)   # False(a 和 b 是不同的对象)

但浅拷贝有陷阱! 如果列表中嵌套了子列表(多维列表),浅拷贝只复制外层,内层的子列表仍然是共享的:

python 复制代码
a = [[1, 2], [3, 4]]
b = a.copy()            # 浅拷贝
b[0][0] = 999           # 修改子列表中的元素
print(a)                # [[999, 2], [3, 4]]  ← a 也被影响了!

为什么? 浅拷贝只复制了"外壳",内部的子列表仍然是同一份引用:

复制代码
浅拷贝前:
a ──> [ [1,2], [3,4] ]

浅拷贝后:
a ──> [ [1,2], [3,4] ]  ← 同一个子列表对象
b ──> [   ↑  ,   ↑   ]  ← b 指向同一个子列表对象

3. deepcopy() ------ 深拷贝(完全独立的副本)

深拷贝会递归地复制所有层级,生成完全独立的副本:

python 复制代码
import copy

a = [[1, 2], [3, 4]]
b = copy.deepcopy(a)    # 深拷贝
b[0][0] = 999
print(a)                # [[1, 2], [3, 4]]  ← a 不受影响
print(b)                # [[999, 2], [3, 4]]

对比总结

方式 代码 修改 b 影响 a? 本质
赋值 = b = a 同一对象,换个名字
浅拷贝 copy() b = a.copy() 看情况:一层不改,多层会改 新外壳,共用内脏
深拷贝 deepcopy() b = copy.deepcopy(a) 不会 完全独立,互不影响

4.2 元组(tuple)

元组是不可变的序列,用圆括号 () 表示。

python 复制代码
point = (3, 5)
rgb = (255, 128, 0)
single = (1,)          # 单元素元组,必须有逗号
# 空元组 ()
empty_tuple = ()

print(point[0])        # 3
print(point[-1])       # 5

x, y = point
print(x, y)            # 3 5

重要:单元素元组必须加逗号!

这是 Python 初学者极易犯的错误。括号 () 在 Python 中有双重含义:既可以表示元组,也可以表示数学运算中的优先级。

python 复制代码
t1 = (5)        # 这不是元组!Python 理解为数学括号,t1 是整数 5
t2 = (5,)       # 这才是单元素元组,逗号是关键
t3 = 5,         # 也可以这样写(元组由逗号定义,括号可省略)

print(type(t1))  # <class 'int'>    ← 不是元组!
print(type(t2))  # <class 'tuple'>  ← 是元组
print(type(t3))  # <class 'tuple'>  ← 也是元组
print(t2[0])     # 5

记忆口诀"元组看逗号,不看括号"。创建元组时,逗号才是关键,括号只是辅助。单元素时必须加逗号,否则 Python 会把它当作普通表达式。
元组 vs 列表

  • 列表可变(可增删改),元组不可变(创建后不能修改)
  • 列表用 [],元组用 ()
  • 元组比列表更安全、更快,适合存储不需要修改的数据

4.3 字典(dict)

字典是键值对(key-value)的集合,用花括号 {} 表示。

python 复制代码
# 创建字典 {键:值, ...}
student = {"name": "张三", "age": 20, "score": 88}

print(student["name"])     # 张三
print(student.get("age"))  # 20(推荐,键不存在返回None,不会报错)

student["score"] = 92      # 修改已有键的值
student["city"] = "北京"   # 添加新键值对

del student["age"]         # 删除键值对

for key in student:                  # 遍历键
    # 打印输出到屏幕
    print(key)

for value in student.values():       # 遍历值
    # 打印输出到屏幕
    print(value)

for key, value in student.items():   # 遍历键值对
    # 打印输出到屏幕
    print(f"{key}: {value}")

print(student.keys())      # 获取所有键
print(student.values())    # 获取所有值
print(student.items())     # 获取所有键值对

4.4 集合(set)

集合是无序不重复元素的集合,用花括号 {} 表示。

python 复制代码
# 创建集合 {...}
numbers = {1, 2, 3, 4, 5}
empty_set = set()         # 注意:{} 是空字典,不是空集合

unique = set([1, 2, 2, 3, 3, 3])  # {1, 2, 3}

# 创建集合 {...}
a = {1, 2, 3, 4}
# 创建集合 {...}
b = {3, 4, 5, 6}

print(a | b)   # 并集:{1, 2, 3, 4, 5, 6}
print(a & b)   # 交集:{3, 4}
print(a - b)   # 差集:{1, 2}
print(a ^ b)   # 对称差集:{1, 2, 5, 6}

4.5 字符串(复习与进阶)

常用字符串方法

方法 功能 示例
len(s) 返回字符串长度 len("Hello")5
s.upper() 转换为大写 "hello".upper()"HELLO"
s.lower() 转换为小写 "Hello".lower()"hello"
s.strip() 去除两端空白 " hello ".strip()"hello"
s.split(sep) 按分隔符分割为列表 "a,b,c".split(",")["a","b","c"]
s.join(list) 将列表用指定分隔符连接为字符串 "-".join(["a","b","c"])"a-b-c"
s.find(sub) 查找子串,返回索引(找不到返回-1) "Hello".find("ll")2
s.replace(old, new) 替换子串 "Hello".replace("l", "x")"Hexxo"
s.count(sub) 统计子串出现次数 "ababa".count("ab")2
s.startswith(prefix) 判断是否以指定字符串开头 "Hello".startswith("He")True
s.endswith(suffix) 判断是否以指定字符串结尾 "Hello".endswith("lo")True
python 复制代码
# 字符串变量 sentence,存储文本
sentence = "Python is fun"
words = sentence.split()          # ["Python", "is", "fun"]
new_sentence = " ".join(words)    # "Python is fun"

# 字符串变量 text,存储文本
text = "I love Python"
print(text.find("love"))          # 2
print(text.replace("Python", "Java"))  # "I love Java"

4.6 序列通用操作

列表、元组、字符串都是序列类型,支持以下通用操作:

操作 描述 示例
x in s 判断x是否在序列s中 3 in [1,2,3]True
x not in s 判断x是否不在序列s中 3 not in [1,2]True
s + t 序列连接 [1,2] + [3][1,2,3]
s * n 序列重复 "Hi" * 3"HiHiHi"
s[i] 索引访问 "abc"[1]"b"
s[i:j] 切片 "abcde"[1:4]"bcd"
len(s) 序列长度 len([1,2,3])3
min(s) 最小值 min([3,1,4])1
max(s) 最大值 max([3,1,4])4
sum(s) 求和(元素必须是数值) sum([1,2,3])6

4.7 列表生成式(推导式)

列表生成式是一种简洁创建列表的语法。

python 复制代码
# 创建列表 [...]
squares = [x ** 2 for x in range(1, 11)]
print(squares)  # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

# 创建列表 [...]
evens = [x for x in range(1, 21) if x % 2 == 0]
print(evens)    # [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

# 创建列表 [...]
pairs = [(x, y) for x in range(1, 4) for y in range(1, 3)]
print(pairs)    # [(1,1), (1,2), (2,1), (2,2), (3,1), (3,2)]

# 创建列表 [...]
words = ["hello", "world", "python"]
# 创建列表 [...]
upper_words = [w.upper() for w in words]
print(upper_words)  # ["HELLO", "WORLD", "PYTHON"]

列表生成式嵌套写法

列表生成式支持嵌套 for 循环,可以一次性生成二维结构。

经典例子:使用列表生成式嵌套生成九九乘法表

python 复制代码
# 外层循环 i = 1 到 9,内层循环 j = 1 到 i
multiplication_table = [f"{j}×{i}={i*j}" for i in range(1, 10) for j in range(1, i+1)]

# 打印结果
for item in multiplication_table:
    print(item, end="\t")
    if item.split("×")[1] == item.split("=")[0]:  # 每行最后一个元素换行
        print()

输出结果

复制代码
1×1=1	
1×2=2	2×2=4	
1×3=3	2×3=6	3×3=9	
1×4=4	2×4=8	3×4=12	4×4=16	
1×5=5	2×5=10	3×5=15	4×5=20	5×5=25	
1×6=6	2×6=12	3×6=18	4×6=24	5×6=30	6×6=36	
1×7=7	2×7=14	3×7=21	4×7=28	5×7=35	6×7=42	7×7=49	
1×8=8	2×8=16	3×8=24	4×8=32	5×8=40	6×8=48	7×8=56	8×8=64	
1×9=9	2×9=18	3×9=27	4×9=36	5×9=45	6×9=54	7×9=63	8×9=72	9×9=81	

嵌套规则:列表生成式中,嵌套 for 的顺序和普通双层 for 循环顺序一致。

python 复制代码
# 等价写法:
result = []
for i in range(1, 10):
    for j in range(1, i+1):
        result.append(f"{j}×{i}={i*j}")

4.8 类型转换

python 复制代码
# 字符串变量 s,存储文本
s = "hello"
print(list(s))              # ['h', 'e', 'l', 'l', 'o']

# 创建列表 [...]
lst = [1, 2, 3]
print(tuple(lst))           # (1, 2, 3)

# 创建列表 [...]
lst2 = [1, 2, 2, 3, 3, 3]
print(set(lst2))            # {1, 2, 3}

# 创建列表 [...]
pairs = [("name", "张三"), ("age", 20)]
print(dict(pairs))          # {"name": "张三", "age": 20}

例题

例题4-1(读程序写结果)

题目:阅读以下程序,写出运行结果。

python 复制代码
# 创建列表 [...]
nums = [10, 20, 30, 40, 50]
# .末尾添加
nums.append(60)
# .插入
nums.insert(0, 5)
# .移除
nums.pop(2)
# .删除
nums.remove(40)
# 打印输出到屏幕
print(nums)

答案/参考代码:

复制代码
[5, 10, 30, 50, 60]

解析

  1. 初始:[10, 20, 30, 40, 50]
  2. append(60)[10, 20, 30, 40, 50, 60]
  3. insert(0, 5)[5, 10, 20, 30, 40, 50, 60]
  4. pop(2) 移除索引2处的元素20 → [5, 10, 30, 40, 50, 60]
  5. remove(40) 移除第一个值为40的元素 → [5, 10, 30, 50, 60]

例题4-2(补全程序题)

题目 :以下程序输入一个英文句子,将其中的每个单词首字母大写后输出(使用字符串的 split()join() 方法)。请补全空缺代码。

python 复制代码
# 输入 → 存入变量 sentence
sentence = input("请输入一个英文句子:")

words = _______  # 将句子按空格分割成单词列表
# 空列表 []
capitalized_words = []
# 遍历 words
for word in words:
    capitalized_words.append(_______ )  # 将每个单词首字母大写

result = _______  # 将列表用空格连接回字符串

# 打印输出到屏幕
print("转换结果:", result)

测试:输入 "hello world python",输出应为 "Hello World Python"

答案/参考代码:

python 复制代码
words = sentence.split()  # 将句子按空格分割成单词列表
# 空列表 []
capitalized_words = []
# 遍历 words
for word in words:
    capitalized_words.append(word.capitalize())  # 将每个单词首字母大写

result = " ".join(capitalized_words)  # 将列表用空格连接回字符串

例题4-3(读程序写结果)

题目:阅读以下程序,写出运行结果。

python 复制代码
t1 = (1, 2, 3)
t2 = (4, 5)
# 计算 → 赋值给 t3
t3 = t1 + t2
# 打印输出到屏幕
print(len(t3))
# 打印输出到屏幕
print(t3[2])
# 打印输出到屏幕
print(t3[-1])
# 打印输出到屏幕
print(t3[1:4])

答案/参考代码:

复制代码
5
3
5
(2, 3, 4)

解析

  • t1 + t2 连接为 (1, 2, 3, 4, 5),长度为5
  • 索引2的元素是 3
  • 最后一个元素(索引-1)是 5
  • t3[1:4] 切片取索引1到3的元素 → (2, 3, 4)

例题4-4(补全程序题)

题目:以下程序统计一个字符串中每个字符出现的次数,存入字典并输出。请补全空缺代码。

python 复制代码
# 输入 → 存入变量 text
text = input("请输入一个字符串:")
char_count = {}  # 空字典

# 遍历 text
for ch in text:
    if _______ :  # 如果字符已经在字典中
        _______  # 计数加1
    # 否则
    else:
        _______  # 否则将字符加入字典,初始为1

# 打印输出到屏幕
print("字符统计结果:")
# .所有键值对
for ch, count in char_count.items():
    # 打印输出到屏幕
    print(f"'{ch}': {count}")

测试:输入 "hello",输出应为 {'h':1, 'e':1, 'l':2, 'o':1}

答案/参考代码:

python 复制代码
# 遍历 text
for ch in text:
    if ch in char_count:    # 如果字符已经在字典中
        char_count[ch] += 1   # 计数加1
    # 否则
    else:
        char_count[ch] = 1    # 否则将字符加入字典,初始为1

例题4-5(读程序写结果)

题目:阅读以下程序,写出运行结果。

python 复制代码
# 创建集合 {...}
set_a = {1, 2, 3, 4, 5}
# 创建集合 {...}
set_b = {4, 5, 6, 7, 8}

# 打印输出到屏幕
print(set_a & set_b)
# 打印输出到屏幕
print(set_a | set_b)
# 打印输出到屏幕
print(set_a - set_b)
# 打印输出到屏幕
print(set_b - set_a)

答案/参考代码:

复制代码
{4, 5}
{1, 2, 3, 4, 5, 6, 7, 8}
{1, 2, 3}
{6, 7, 8}

解析

  • 交集 &:两个集合共有的元素 → {4, 5}
  • 并集 |:两个集合所有不重复的元素 → {1,2,3,4,5,6,7,8}
  • 差集 -:在set_a中但不在set_b中的元素 → {1,2,3}
  • 差集 -:在set_b中但不在set_a中的元素 → {6,7,8}

例题4-6(读程序写结果)

题目:阅读以下程序,写出运行结果。

python 复制代码
# 创建列表 [...]
numbers = [x * 2 for x in range(1, 6) if x % 2 == 0]
# 打印输出到屏幕
print(numbers)

# 创建列表 [...]
words = ["python", "java", "c++", "go"]
# 创建列表 [...]
result = [w.upper() for w in words if len(w) > 3]
# 打印输出到屏幕
print(result)

答案/参考代码:

复制代码
[4, 8]
['PYTHON', 'JAVA']

解析

  1. 第一个列表生成式:range(1,6) 中偶数为2和4,乘以2后得到 [4, 8]
  2. 第二个列表生成式:长度大于3的单词有 "python"(6) 和 "java"(4),转换为大写

例题4-7(补全程序题)

题目:以下程序使用字典存储学生的各科成绩,遍历字典计算总分和平均分。请补全空缺代码。

python 复制代码
# 创建字典 {键:值, ...}
scores = {"语文": 85, "数学": 92, "英语": 78, "Python": 95}

# 整型变量 total = 0
total = 0
for _______ :  # 遍历字典的值
    # total += 运算
    total += score

# 计算 → 赋值给 average
average = total / _______

# 打印输出到屏幕
print(f"总分:{total}")
# 打印输出到屏幕
print(f"平均分:{average:.1f}")

答案/参考代码:

python 复制代码
for score in scores.values():  # 遍历字典的值
    # total += 运算
    total += score

# 计算 → 赋值给 average
average = total / len(scores)

例题4-8(编程题)

题目描述

编写程序,输入一段英文文本,统计其中每个单词出现的次数(不区分大小写),并按照单词的字母顺序输出统计结果。要求:

  • 使用字典存储单词及其出现次数
  • 使用字符串的 split()lower()strip() 方法处理文本
  • 输出格式:每行一个单词及其出现次数

参考代码

python 复制代码
# 输入 → 存入变量 text
text = input("请输入一段英文文本:")

# 导入 string 库
import string
# 遍历 text
for ch in text:
    # 如果 ch in string.punctuation
    if ch in string.punctuation:
        # .替换
        text = text.replace(ch, " ")

# .转小写
words = text.lower().split()

# 空字典 {}
word_count = {}
# 遍历 words
for word in words:
    # 如果 word in word_count
    if word in word_count:
        word_count[word] += 1
    # 否则
    else:
        word_count[word] = 1

# 打印输出到屏幕
print("\n单词统计结果:")
# .所有键
for word in sorted(word_count.keys()):
    # 打印输出到屏幕
    print(f"{word}: {word_count[word]}")

4.9 考试提示

针对"读程序写结果"题型

  • 列表操作 的链式调用:appendinsertpopremove 等方法的顺序执行结果
  • 集合运算 :交集 &、并集 |、差集 - 的结果
  • 列表生成式的等价展开要能理解
  • 元组的不可变性特点常结合索引和切片出题

针对"补全程序题"题型

  • 字典统计 模式:if key in dict: dict[key] += 1 else: dict[key] = 1
  • 字符串方法split()join()upper()lower()capitalize()
  • 字典遍历items()keys()values() 的区分使用

针对"编程题"题型

  • 词频统计是典型考题,需要综合使用字符串方法和字典
  • 列表去重 :使用 set() 或自定义逻辑
  • 数据分组:使用字典将数据按照某种规则分组存储

课后练习

课后练习4-1(读程序写结果)

题目:阅读以下程序,写出运行结果。

python 复制代码
# 创建列表 [...]
data = [3, 1, 4, 1, 5, 9, 2, 6]
# .排序
data.sort()
# .移除
data.pop()
# .插入
data.insert(0, 0)
# .追加多个
data.extend([7, 8])
# 打印输出到屏幕
print(data)

答案

复制代码
[0, 1, 1, 2, 3, 4, 5, 6, 7, 8]

解析

  1. 初始:[3, 1, 4, 1, 5, 9, 2, 6]
  2. sort() 排序:[1, 1, 2, 3, 4, 5, 6, 9]
  3. pop() 移除最后一个元素9:[1, 1, 2, 3, 4, 5, 6]
  4. insert(0, 0) 在开头插入0:[0, 1, 1, 2, 3, 4, 5, 6]
  5. extend([7, 8]) 追加7和8:[0, 1, 1, 2, 3, 4, 5, 6, 7, 8]

课后练习4-2(补全程序题)

题目:以下程序输入一个列表(元素为整数),去除列表中的重复元素(保持原有顺序),并输出结果。请补全空缺代码。

python 复制代码
nums = eval(input("请输入一个整数列表,如[1,2,3]:"))

result = []  # 存放去重后的结果

# 遍历 nums
for num in nums:
    if _______ :  # 如果num不在result中
        _______   # 将num加入result

# 打印输出到屏幕
print("去重后的结果:", result)

测试:输入 [1, 2, 2, 3, 1, 4, 3],输出应为 [1, 2, 3, 4]

答案

python 复制代码
# 遍历 nums
for num in nums:
    if num not in result:  # 如果num不在result中
        result.append(num) # 将num加入result

课后练习4-3(编程题)

题目描述

编写程序,模拟一个电话簿管理系统。要求:

  1. 使用字典存储联系人信息,格式为 {名称: 电话号码}
  2. 程序首先添加3个联系人(名称和号码由用户输入)
  3. 输入一个联系人名称,查询其电话号码
  4. 如果联系人存在则输出号码,不存在则输出"未找到该联系人"
  5. 最后输出所有联系人的列表(按名称字母顺序排序)

参考代码

python 复制代码
# 空字典 {}
phonebook = {}

# 打印输出到屏幕
print("请添加3个联系人:")
# 遍历 range(3)
for i in range(3):
    # 输入 → 存入变量 name
    name = input(f"请输入第{i+1}个联系人的名称:")
    # 输入 → 存入变量 phone
    phone = input(f"请输入{name}的电话号码:")
    phonebook[name] = phone

# 输入 → 存入变量 search_name
search_name = input("\n请输入要查询的联系人名称:")
# 如果 search_name in phonebook
if search_name in phonebook:
    # 打印输出到屏幕
    print(f"{search_name}的电话号码是:{phonebook[search_name]}")
# 否则
else:
    # 打印输出到屏幕
    print("未找到该联系人")

# 打印输出到屏幕
print("\n所有联系人列表(按名称排序):")
# .所有键
for name in sorted(phonebook.keys()):
    # 打印输出到屏幕
    print(f"{name}: {phonebook[name]}")

本章模拟练习

模拟练习4-1(读程序写结果)

题目:阅读以下程序,写出运行结果。

python 复制代码
students = [
    {"name": "张三", "score": 85},
    {"name": "李四", "score": 92},
    {"name": "王五", "score": 78}
]

# 整型变量 max_score = 0
max_score = 0
# 字符串变量 max_name,存储文本
max_name = ""
# 遍历 students
for s in students:
    # 如果 s["score"] > max_score
    if s["score"] > max_score:
        max_score = s["score"]
        max_name = s["name"]

# 打印输出到屏幕
print(f"最高分:{max_name},{max_score}分")

答案

复制代码
最高分:李四,92分

解析:遍历字典列表,记录最高分和对应的姓名。张三85分、李四92分、王五78分,最高分是李四的92分。


模拟练习4-2(补全程序题)

题目:以下程序输入一个字符串,判断它是否为回文字符串(正读反读一样,如"abcba")。请补全空缺代码。

python 复制代码
# 输入 → 存入变量 text
text = input("请输入一个字符串:")

reversed_text = _______  # 反转字符串
# 如果 text == reversed_text
if text == reversed_text:
    # 打印输出到屏幕
    print(f"'{text}'是回文字符串")
# 否则
else:
    # 打印输出到屏幕
    print(f"'{text}'不是回文字符串")

char_list = list(text)
# .反转
char_list.reverse()
reversed_text2 = _______  # 将列表连接回字符串
# 打印输出到屏幕
print(f"方法二验证:'{reversed_text2}'")

答案

python 复制代码
reversed_text = text[::-1]  # 反转字符串

reversed_text2 = "".join(char_list)  # 将列表连接回字符串

模拟练习4-3(编程题)

题目描述

编写程序,对学生成绩进行统计分析。要求:

  1. 输入若干个学生的成绩(用空格分隔,如 85 92 78 90 88
  2. 将输入的字符串转换为成绩列表
  3. 计算并输出:
    • 最高分、最低分、平均分(保留1位小数)
    • 优秀(≥90分)的学生人数
    • 不及格(<60分)的学生人数
  4. 输出所有成绩中位数(排序后取中间的那个数,如果为偶数个则取中间两个的平均值)

参考代码

python 复制代码
# 输入 → 存入变量 input_str
input_str = input("请输入学生成绩(用空格分隔):")
# .分割
score_strs = input_str.split()
scores = [float(s) for s in score_strs]  # 列表生成式转换为浮点数

max_score = max(scores)
min_score = min(scores)
# 计算 → 赋值给 avg_score
avg_score = sum(scores) / len(scores)

excellent = len([s for s in scores if s >= 90])
fail = len([s for s in scores if s < 60])

sorted_scores = sorted(scores)
n = len(sorted_scores)
if n % 2 == 1:  # 奇数个
    # 计算 → 赋值给 median
    median = sorted_scores[n // 2]
else:           # 偶数个
    # 计算 → 赋值给 median
    median = (sorted_scores[n // 2 - 1] + sorted_scores[n // 2]) / 2

# 打印输出到屏幕
print(f"成绩列表:{scores}")
# 打印输出到屏幕
print(f"最高分:{max_score}")
# 打印输出到屏幕
print(f"最低分:{min_score}")
# 打印输出到屏幕
print(f"平均分:{avg_score:.1f}")
# 打印输出到屏幕
print(f"优秀人数(≥90分):{excellent}")
# 打印输出到屏幕
print(f"不及格人数(<60分):{fail}")
# 打印输出到屏幕
print(f"中位数:{median}")

下一章预告:第5章 函数与模块 ------ 将学习函数的定义与调用、参数传递、变量作用域、模块导入等知识。

Python程序设计 期末备考教程(第5-8章)



4.8 综合应用:二分查找法

二分查找法在有序列表中快速查找元素,每次将查找范围缩小一半。

算法步骤

  1. 设定low=0指向第一个元素,high=len(lst)-1指向最后一个
  2. 计算中间位置 mid = (low + high) // 2
  3. 比较目标值与lst[mid]
    • 相等 → 找到,返回索引
    • 目标值 < lst[mid]high = mid - 1(搜索左半部分)
    • 目标值 > lst[mid]low = mid + 1(搜索右半部分)
  4. 重复2~3步,直到low > high为止

代码实现

python 复制代码
def binary_search(lst, target):
    """在有序列表lst中查找target,返回索引,未找到返回-1"""
    low, high = 0, len(lst) - 1
    
    while low <= high:
        mid = (low + high) // 2
        if lst[mid] == target:
            return mid
        elif target < lst[mid]:
            high = mid - 1
        else:
            low = mid + 1
    
    return -1

# 测试
nums = [1, 3, 5, 7, 9, 11, 13, 15]
print(binary_search(nums, 7))   # 输出:3
print(binary_search(nums, 4))   # 输出:-1

复杂度分析

维度 对比
时间复杂度 O(log n) 顺序查找O(n)
空间复杂度 O(1) 只需几个指针变量

二分查找要求列表有序 且支持随机访问(如Python列表)。

示例 :在升序列表 [2, 5, 8, 12, 16, 23, 38, 45, 50] 中查找23:

  • low=0, high=8, mid=4lst[4]=16 < 23low=5
  • low=5, high=8, mid=6lst[6]=38 > 23high=5
  • low=5, high=5, mid=5lst[5]=23 → 找到!

第5章 编程思维与方法

一、知识讲解

1. 计数统计

计数统计是最基本的编程思维,通过循环遍历数据,使用计数器变量统计满足条件的元素个数。

python 复制代码
# 基本框架
# 声明整型变量 count,赋值为 0
count = 0
# for 循环:遍历 数据集合 中的每个元素
for 元素 in 数据集合:
    # if 判断 条件 是否成立
    if 条件:
        # 将 count 进行 += 运算并赋值回自身
        count += 1

2. 穷举法

穷举法(暴力枚举)是将所有可能的情况逐一列举,检查每种情况是否满足条件。

python 复制代码
# 基本框架
# for 循环:遍历 range(起始, 结束+1) 中的每个元素
for i in range(起始, 结束+1):
    # if 判断 条件 是否成立
    if 条件:
        print(i)  # 输出满足条件的解

典型应用:水仙花数、质数判断、百钱百鸡等。

3. 递推/迭代

递推是从已知条件出发,逐步推导出结果。迭代是不断用旧值计算新值的过程。

python 复制代码
# 斐波那契数列递推
a, b = 1, 1
# for 循环:遍历 range(2, n) 中的每个元素
for i in range(2, n):
    # 计算表达式,结果赋值给 a, b
    a, b = b, a + b

4. 顺序查找

从列表的第一个元素开始,依次比较,直到找到目标或遍历完所有元素。

python 复制代码
def sequential_search(lst, target):
    # for 循环:遍历 range(len(lst)) 中的每个元素
    for i in range(len(lst)):
        # if 判断 lst[i] == target 是否成立
        if lst[i] == target:
            return i  # 返回下标
    return -1  # 未找到

5. 二分查找

二分查找要求数据必须是有序的。每次取中间元素与目标比较,缩小一半查找范围。

python 复制代码
def binary_search(lst, target):
    # 计算表达式,结果赋值给 low, high
    low, high = 0, len(lst) - 1
    # while 循环:当 low <= high 成立时重复执行
    while low <= high:
        # 计算表达式,结果赋值给 mid
        mid = (low + high) // 2
        # if 判断 lst[mid] == target 是否成立
        if lst[mid] == target:
            return mid
        # elif 再判断 lst[mid] < target 是否成立
        elif lst[mid] < target:
            # 计算表达式,结果赋值给 low
            low = mid + 1
        # else 以上条件都不成立时执行
        else:
            # 计算表达式,结果赋值给 high
            high = mid - 1
    return -1

6. 冒泡排序

相邻元素两两比较,大的往后"冒泡"。每轮确定一个最大值放在末尾。

python 复制代码
def bubble_sort(lst):
    n = len(lst)                    # n = 列表的长度(即元素的个数)
    for i in range(n - 1):          # 外层循环:总共需要 n-1 轮比较
        for j in range(n - 1 - i):  # 内层循环:每轮比较的次数递减(已冒泡的不再比较)
            if lst[j] > lst[j + 1]: # 如果前面的元素比后面的大(顺序不对)
                lst[j], lst[j + 1] = lst[j + 1], lst[j]  # 交换两个元素,把大的往后"冒泡"

7. 选择排序

每轮选出未排序部分的最小值,放到已排序部分的末尾。

python 复制代码
def selection_sort(lst):
    n = len(lst)                        # n = 列表的长度
    for i in range(n - 1):              # 外层循环:每一轮选出一个最小值放到位置 i
        min_idx = i                     # 先假设位置 i 的元素就是本轮的最小值
        for j in range(i + 1, n):       # 内层循环:遍历位置 i 之后的所有未排序元素
            if lst[j] < lst[min_idx]:   # 如果发现更小的元素
                min_idx = j             # 更新最小值下标为 j
        lst[i], lst[min_idx] = lst[min_idx], lst[i]  # 将最小值与位置 i 的元素交换

8. 插入排序

将未排序元素逐个插入到已排序部分的正确位置。

python 复制代码
def insertion_sort(lst):
    n = len(lst)                    # n = 列表的长度
    for i in range(1, n):           # 从第2个元素开始,逐个插入已排序的部分
        key = lst[i]                # key = 当前要插入的元素(好比手里的"待插入牌")
        j = i - 1                   # j 指向已排序部分的最后一个元素
        while j >= 0 and lst[j] > key:  # 从右向左找插入位置,只要前面的比 key 大就继续
            lst[j + 1] = lst[j]     # 把比 key 大的元素往后移一位
            j -= 1                  # j 继续向左移动,检查更前面的元素
        lst[j + 1] = key            # 找到正确位置,把 key 插入进去

9. 希尔排序(Shell Sort)

希尔排序是插入排序的改进版,通过引入"增量"(gap)的概念,先对间隔较大的元素进行排序,使数组"基本有序",再逐步缩小增量进行插入排序,最终 gap=1 时就是一次标准的插入排序。

python 复制代码
def shell_sort(lst):
    n = len(lst)
    gap = n // 2                    # 初始增量 gap = 列表长度的一半
    while gap > 0:                  # 当 gap 大于 0 时继续循环
        for i in range(gap, n):     # 从 gap 位置开始,对每个元素进行"分组插入排序"
            temp = lst[i]           # 取出当前要插入的元素
            j = i
            while j >= gap and lst[j - gap] > temp:  # 在 gap 间隔分组中向前比较
                lst[j] = lst[j - gap]                 # 将较大的元素向后移动一个 gap 位
                j -= gap                              # j 向前移动 gap 位
            lst[j] = temp           # 找到正确位置,插入
        gap //= 2                   # 增量减半,缩小分组间距

希尔排序执行示例

复制代码
原始列表:[8, 9, 1, 7, 2, 3, 5, 4, 6, 0]

gap=5:  [3, 5, 1, 6, 0, 8, 9, 4, 7, 2]   (每隔5个元素的子序列分别排序)
gap=2:  [0, 2, 1, 4, 3, 5, 7, 6, 8, 9]   (间隔2的分组排序)
gap=1:  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]   (标准插入排序,此时列表已基本有序)

10. 排序算法对比

排序算法 最好情况 平均情况 最坏情况 空间复杂度 稳定性
冒泡排序 O(n) O(n²) O(n²) O(1) 稳定
选择排序 O(n²) O(n²) O(n²) O(1) 不稳定
插入排序 O(n) O(n²) O(n²) O(1) 稳定
希尔排序 O(nlogn) O(n^1.3) O(n²) O(1) 不稳定
快速排序 O(nlogn) O(nlogn) O(n²) O(logn) 不稳定
  • 稳定排序:值相等的元素在排序后相对位置不变。冒泡排序和插入排序是稳定的。
  • 不稳定排序:值相等的元素在排序后可能会交换位置。选择排序和希尔排序是不稳定的。
  • 快速排序 是实际应用中最常用的排序算法,平均效率最高,Python 内置的 sorted()list.sort() 底层使用的 Timsort 就是归并排序和插入排序的混合优化版本。

11. 时间复杂度

时间复杂度用来衡量算法执行时间随数据规模增长的变化趋势。使用大O表示法(Big O Notation)来描述。

常见复杂度从小到大排序

复制代码
O(1) < O(logn) < O(n) < O(nlogn) < O(n²) < O(n³) < O(2ⁿ)
复杂度 名称 通俗解释 典型例子
O(1) 常数阶 无论数据多大,执行时间不变 列表索引访问 lst[i]
O(logn) 对数阶 每次操作后数据规模减半 二分查找
O(n) 线性阶 执行时间与数据规模成正比 顺序查找、单层循环
O(nlogn) 线性对数阶 比线性稍慢,但优于平方阶 快速排序、归并排序
O(n²) 平方阶 嵌套循环,数据翻倍时间翻四倍 冒泡排序、选择排序、插入排序
O(n³) 立方阶 三重嵌套循环 简单矩阵乘法
O(2ⁿ) 指数阶 数据每增加1,时间翻倍 递归求斐波那契(无优化)

如何判断时间复杂度

python 复制代码
# O(1) - 常数阶:只执行一次
x = lst[0]

# O(n) - 线性阶:一个循环
for i in range(n):
    print(i)

# O(n²) - 平方阶:嵌套循环
for i in range(n):        # 外层循环 n 次
    for j in range(n):    # 内层循环 n 次
        print(i, j)       # 总共执行 n × n 次

# O(logn) - 对数阶:每次减半
while n > 1:
    n = n // 2            # 每次 n 缩小一半,大约执行 log₂n 次

考试重点:记住冒泡排序、选择排序、插入排序的时间复杂度都是 O(n²),二分查找是 O(logn)。看到嵌套循环就是 O(n²),单层循环就是 O(n)。

12. sorted()sort() 的区别

这是一个非常高频的考点,务必区分清楚:

对比维度 list.sort() sorted()
类型 列表的方法(只能用于列表) 内置函数(可用于任何可迭代对象)
是否改变原数据 原地排序,改变原列表 生成新列表,不改变原数据
返回值 None(无返回值) 返回一个新的排序后的列表
用法 lst.sort() new_lst = sorted(lst)

代码对比

python 复制代码
# ---- sort():原地排序,改变原列表 ----
numbers = [3, 1, 4, 1, 5]
result = numbers.sort()
print(result)   # None(sort() 没有返回值!)
print(numbers)  # [1, 1, 3, 4, 5](原列表被改变了)

# ---- sorted():生成新列表,不改变原数据 ----
numbers = [3, 1, 4, 1, 5]
result = sorted(numbers)
print(result)   # [1, 1, 3, 4, 5](返回新列表)
print(numbers)  # [3, 1, 4, 1, 5](原列表不变!)

# ---- sorted() 可用于任何可迭代对象 ----
# 对元组排序
t = (3, 1, 4, 1, 5)
print(sorted(t))          # [1, 1, 3, 4, 5](返回列表)

# 对字符串排序
print(sorted("python"))   # ['h', 'n', 'o', 'p', 't', 'y'](按字母顺序)

# 降序排序
print(sorted(numbers, reverse=True))  # [5, 4, 3, 1, 1]
numbers.sort(reverse=True)
print(numbers)                         # [5, 4, 3, 1, 1]

记忆口诀"sort 改自己,sorted 生新列"sort() 是列表的方法,调用后原列表被修改且返回 Nonesorted() 是内置函数,不改变原数据,返回一个新的排序列表。考试中经常在"读程序写结果"题型中考察这一点------如果误以为 sort() 有返回值,就会写错结果。


二、例题精讲

【读程序写结果】例题1:穷举法

题目:阅读以下程序,写出运行结果。

python 复制代码
count = 0                          # count 用来统计符合条件的数字个数,初始为 0
for i in range(100, 200):          # 遍历 100 到 199 之间的每一个整数
    if i % 3 == 0 and i % 5 == 0:  # 如果 i 能同时被 3 和 5 整除(即能被 15 整除)
        count += 1                  # 计数器加 1
print(count)                       # 输出满足条件的数字总个数

答案:7

解析:程序统计了100到199之间能同时被3和5整除(即能被15整除)的数的个数。100÷15≈6.67,所以第一个数是105,最后一个是195,共有(195-105)/15+1=7个。


【读程序写结果】例题2:递推法

题目:阅读以下程序,写出运行结果。

python 复制代码
a, b = 1, 2              # 初始化 a = 1, b = 2(a 在前,b 在后)
for i in range(4):       # 循环执行 4 次(i = 0, 1, 2, 3)
    a, b = b, a + b      # 同时更新:新 a = 旧 b,新 b = 旧 a + 旧 b
print(a)                 # 循环结束后输出 a 的值

答案:13

解析:这是一个递推过程。

  • 初始:a=1, b=2
  • i=0:a=2, b=1+2=3
  • i=1:a=3, b=2+3=5
  • i=2:a=5, b=3+5=8
  • i=3:a=8, b=5+8=13
    循环结束打印a,值为13。

【补全程序题】例题3:穷举法补全

题目:百钱百鸡问题:公鸡5元一只,母鸡3元一只,小鸡1元三只。用100元买100只鸡,请补全以下程序输出所有购买方案。

python 复制代码
for x in range(0, 21):        # x 表示公鸡数量(最多20只)
    for y in range(0, 34):    # y 表示母鸡数量(最多33只)
        z = ________          # 小鸡数量 = 100 - x - y
        if 5 * x + 3 * y + ________ == 100:  # 总价等于100
            # print() 输出内容到屏幕上
            print(f"公鸡{x}只,母鸡{y}只,小鸡{z}只")

答案

  • 第1空:100 - x - y
  • 第2空:z / 3z // 3

参考代码

python 复制代码
for x in range(0, 21):           # x 遍历公鸡的可能数量:0 ~ 20 只
    for y in range(0, 34):       # y 遍历母鸡的可能数量:0 ~ 33 只
        z = 100 - x - y          # z = 小鸡数量 = 总数 100 - 公鸡 - 母鸡
        if z >= 0 and 5 * x + 3 * y + z / 3 == 100:  # 钱数总和必须等于 100 元,且小鸡数 >= 0
            print(f"公鸡{x}只,母鸡{y}只,小鸡{z}只")  # 输出符合条件的购买方案

【补全程序题】例题4:二分查找补全

题目 :以下程序用二分查找在有序列表 [1, 3, 5, 7, 9, 11, 13, 15, 17, 19] 中查找数字 13,请补全空缺。

python 复制代码
# 创建列表,用方括号 [] 包裹元素
lst = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
# 声明整型变量 target,赋值为 13
target = 13
low, high = 0, ________

# while 循环:当 low <= high 成立时重复执行
while low <= high:
    mid = ________
    # if 判断 lst[mid] == target 是否成立
    if lst[mid] == target:
        # print() 输出内容到屏幕上
        print(f"找到了,下标为{mid}")
        ________
    # elif 再判断 lst[mid] < target 是否成立
    elif lst[mid] < target:
        # 计算表达式,结果赋值给 low
        low = mid + 1
    # else 以上条件都不成立时执行
    else:
        # 计算表达式,结果赋值给 high
        high = mid - 1

答案

  • 第1空:len(lst) - 1(右边界初始值)
  • 第2空:(low + high) // 2(计算中间下标)
  • 第3空:break(找到后退出循环)

【补全程序题】例题5:选择排序补全

题目:以下程序使用选择排序将列表升序排列,请补全空缺。

python 复制代码
# 创建列表,用方括号 [] 包裹元素
lst = [64, 25, 12, 22, 11]
n = len(lst)

# for 循环:遍历 range(n - 1) 中的每个元素
for i in range(n - 1):
    min_idx = ________
    # for 循环:遍历 range(i + 1, n) 中的每个元素
    for j in range(i + 1, n):
        # if 判断 lst[j] ________ lst[min_idx] 是否成立
        if lst[j] ________ lst[min_idx]:
            min_idx = ________
    lst[i], lst[min_idx] = lst[min_idx], lst[i]

# print() 输出内容到屏幕上
print(lst)

答案

  • 第1空:i(将当前下标设为最小值下标)
  • 第2空:<(发现更小的值)
  • 第3空:j(更新最小值下标为j)

【编程题】例题6:冒泡排序编程

题目 :编写程序,使用冒泡排序将列表 [5, 2, 8, 1, 9, 4] 按升序排列,并输出排序后的结果。

参考代码

python 复制代码
lst = [5, 2, 8, 1, 9, 4]    # 原始未排序的列表
n = len(lst)                 # n = 列表的长度

for i in range(n - 1):       # 外层循环:总共需要 n-1 轮排序
    for j in range(n - 1 - i):   # 内层循环:每轮比较 n-1-i 对相邻元素
        if lst[j] > lst[j + 1]:  # 如果左边的数比右边的大
            lst[j], lst[j + 1] = lst[j + 1], lst[j]  # 交换位置,把较大的数往后移

print("排序结果:", lst)       # 输出排序完成后的列表

输出排序结果: [1, 2, 4, 5, 8, 9]


【编程题】例题7:顺序查找编程

题目 :编写程序,统计列表 [78, 56, 92, 45, 88, 92, 67, 92] 中指定分数 92 出现的次数,并输出所有出现位置的下标。

参考代码

python 复制代码
lst = [78, 56, 92, 45, 88, 92, 67, 92]  # 待查找的成绩列表
target = 92                               # 目标值:要查找的分数
count = 0                                 # 计数器:记录目标值出现的次数
positions = []                            # 空列表:用来存放所有出现位置的下标

for i in range(len(lst)):                 # 遍历列表的每一个下标位置
    if lst[i] == target:                  # 如果当前位置的元素等于目标值
        count += 1                        # 次数加 1
        positions.append(i)               # 把这个位置的下标存到列表中

print(f"目标值 {target} 出现了 {count} 次")     # 输出总共出现的次数
print(f"出现的下标位置为: {positions}")          # 输出所有出现位置的下标

输出

复制代码
目标值 92 出现了 3 次
出现的下标位置为: [2, 5, 7]

【编程题】例题8:迭代法编程

题目:编写程序,使用迭代法计算斐波那契数列的第20项。斐波那契数列定义:第1项为1,第2项为1,从第3项开始每项等于前两项之和。

参考代码

python 复制代码
n = 20                   # 要求第 20 项
a, b = 1, 1              # 斐波那契数列的前两项都是 1

for i in range(2, n):    # 从第 3 项开始循环到第 20 项(循环 18 次)
    a, b = b, a + b      # 同时更新:新 a = 旧 b(前一项),新 b = 旧 a + 旧 b(当前项)

print(f"斐波那契数列第 {n} 项为: {b}")  # 输出第 20 项的值

输出斐波那契数列第 20 项为: 6765


三、考试提示

针对"读程序写结果"题型

  • 穷举法题:重点关注循环范围和判断条件,手动模拟几轮循环即可找到规律。
  • 递推法题:建议用表格记录每次循环后的变量值变化,找出最终结果。
  • 排序法题:可以手动画出每次交换后的列表状态,避免出错。

针对"补全程序题"题型

  • 二分查找常考的三个空:high初值(len(lst)-1)、mid计算((low+high)//2)、找到后break
  • 选择排序常考:min_idx初值i、比较条件<、更新min_idx为j
  • 注意补全时不要写多余的符号,空格也要规范。

针对"编程题"题型

  • 冒泡排序:记住双重循环结构,外层n-1次,内层n-1-i次。
  • 顺序查找:遍历每个元素比较即可,注意返回下标或统计次数。
  • 迭代法:记住递推公式,用a, b = b, a+b同时更新两个变量。
  • 做题步骤:先写注释理清思路 → 写框架代码 → 填充细节 → 检查边界条件。

四、课后练习

练习1(读程序写结果)

题目:阅读以下程序,写出运行结果。

python 复制代码
count = 0                        # 计数器初始化为 0
for i in range(1, 51):           # 遍历 1 到 50 之间的每个整数
    if i % 7 == 0 or i % 10 == 7:  # 如果 i 是 7 的倍数,或者个位数字是 7
        count += 1                # 计数器加 1
print(count)                     # 输出符合条件的数字总个数

答案:17

解析:统计1到50之间7的倍数或个位是7的数字个数。

  • 7的倍数:7,14,21,28,35,42,49 → 7个
  • 个位是7但不是7的倍数:17,27,37,47 → 4个
    但注意7既是7的倍数又个位是7,已被计数。实际上程序是or,所以每个数只计一次。
  • 7的倍数:7,14,21,28,35,42,49(7个)
  • 个位是7:7,17,27,37,47(5个,但7已算过,新增4个)
  • 总共:7+4 = 11... 等一下,让我重新算。

7的倍数(1-50):7,14,21,28,35,42,49 → 7个

个位是7:7,17,27,37,47 → 5个

其中7是交集(重复)

所以 count = 7 + 5 - 1 = 11... 不对!程序用的是 or,每个i判断一次。让我数:

i=7: 7%70 ✓ → count=1
i=14: 14%70 ✓ → count=2

i=17: 17%10==7 ✓ → count=3

i=21: ✓ → count=4

i=27: ✓ → count=5

i=28: ✓ → count=6

i=35: ✓ → count=7

i=37: ✓ → count=8

i=42: ✓ → count=9

i=47: ✓ → count=10

i=49: ✓ → count=11

所以答案是11。

等等,再检查一下:

7: 7%70 ✓
14: 14%70 ✓

17: 17%107 ✓
21: 21%70 ✓

27: 27%107 ✓
28: 28%70 ✓

35: 35%70 ✓
37: 37%107 ✓

42: 42%70 ✓
47: 47%107 ✓

49: 49%7==0 ✓

一共11个。


练习2(补全程序题)

题目:以下程序使用顺序查找在列表中查找目标值,请补全空缺。

python 复制代码
# 创建列表,用方括号 [] 包裹元素
scores = [88, 72, 93, 65, 85, 90]
# 声明整型变量 target,赋值为 85
target = 85
found = ________

# for 循环:遍历 range(________) 中的每个元素
for i in range(________):
    # if 判断 scores[i] == target 是否成立
    if scores[i] == target:
        # print() 输出内容到屏幕上
        print(f"找到 {target},下标为 {i}")
        # 声明布尔型变量 found,值为 True(真)
        found = True
        ________

# if 判断 not found 是否成立
if not found:
    # print() 输出内容到屏幕上
    print("未找到")

答案

  • 第1空:False
  • 第2空:len(scores)
  • 第3空:break

练习3(编程题)

题目:编写程序,使用穷举法找出100到999之间所有的"水仙花数"。水仙花数是指一个三位数,其各位数字的立方和等于该数本身。例如:153 = 1^3 + 5^3 + 3^3。

参考代码

python 复制代码
for num in range(100, 1000):           # 遍历所有三位数(100 ~ 999)
    a = num // 100                      # 百位:整除 100 取商,如 345//100 = 3
    b = (num // 10) % 10               # 十位:先整除 10 去掉个位,再取个位,如 (345//10)%10 = 4
    c = num % 10                        # 个位:除以 10 取余数,如 345%10 = 5
    if a**3 + b**3 + c**3 == num:      # 如果百位³ + 十位³ + 个位³ 等于原数本身
        print(num, end=" ")             # 则输出该水仙花数,末尾用空格隔开不换行

输出153 370 371 407


五、本章模拟练习

模拟1(读程序写结果)

题目:阅读以下程序,写出运行结果。

python 复制代码
lst = [3, 7, 1, 9, 4, 6, 2]   # 原始未排序的列表
n = len(lst)                    # n = 列表长度
for i in range(n - 1):          # 外层循环:进行 n-1 轮冒泡排序
    for j in range(n - 1 - i):  # 内层循环:每轮比较次数递减
        if lst[j] > lst[j + 1]: # 如果前面的数比后面大
            lst[j], lst[j + 1] = lst[j + 1], lst[j]  # 交换两者,让大的往后走
print(lst)                     # 输出排序后的结果

答案[1, 2, 3, 4, 6, 7, 9]


模拟2(补全程序题)

题目:以下程序使用二分查找在有序列表中查找目标值,请补全程序。

python 复制代码
# 创建列表,用方括号 [] 包裹元素
lst = [2, 5, 8, 12, 16, 23, 38, 45, 56, 72]
# 声明整型变量 target,赋值为 23
target = 23
# 声明整型变量 low,赋值为 0
low = 0
high = ________

# while 循环:当 low <= high 成立时重复执行
while low <= high:
    mid = ________
    # if 判断 lst[mid] == target 是否成立
    if lst[mid] == target:
        # print() 输出内容到屏幕上
        print(f"找到 {target},下标为 {mid}")
        ________
    # elif 再判断 lst[mid] < target 是否成立
    elif lst[mid] < target:
        ________
    # else 以上条件都不成立时执行
    else:
        # 计算表达式,结果赋值给 high
        high = mid - 1

答案

  • 第1空:len(lst) - 1
  • 第2空:(low + high) // 2
  • 第3空:break
  • 第4空:low = mid + 1

模拟3(编程题)

题目 :编写程序,使用选择排序将列表 [29, 10, 14, 37, 13, 33] 按升序排列,每完成一轮排序后输出当前列表状态。

参考代码

python 复制代码
lst = [29, 10, 14, 37, 13, 33]  # 原始未排序列表
n = len(lst)                     # n = 列表长度

for i in range(n - 1):           # 外层循环:进行 n-1 轮选择
    min_idx = i                  # 假设位置 i 的元素是当前最小值
    for j in range(i + 1, n):    # 内层循环:从 i+1 开始向后查找更小的元素
        if lst[j] < lst[min_idx]:  # 如果找到了更小的
            min_idx = j          # 更新最小值下标
    lst[i], lst[min_idx] = lst[min_idx], lst[i]  # 将最小值交换到位置 i
    print(f"第{i+1}轮: {lst}")   # 每轮结束后输出当前列表状态

print("最终结果:", lst)          # 输出最终排序结果

输出

复制代码
第1轮: [10, 29, 14, 37, 13, 33]
第2轮: [10, 13, 14, 37, 29, 33]
第3轮: [10, 13, 14, 37, 29, 33]
第4轮: [10, 13, 14, 29, 37, 33]
第5轮: [10, 13, 14, 29, 33, 37]
最终结果: [10, 13, 14, 29, 33, 37]


算法分类与复杂度分析

这是计算机算法的基础分析框架,用来评估和比较不同算法的优劣。

算法分类汇总

算法类别 核心思路 典型例题
递推法 从已知条件出发,利用递推式逐步推出后面的结果 斐波那契数列、猴子吃桃
迭代法 从初始近似值出发,反复用公式改进逼近精确解 牛顿迭代法求平方根
穷举法 逐一列举所有可能的情况,检查是否满足条件 百钱百鸡、水仙花数、素数
查找算法 在数据集合中查找特定元素的位置 顺序查找、二分查找
排序算法 将一组数据按指定顺序重新排列 选择排序、插入排序、冒泡排序

时间复杂度

算法的时间复杂度记作 T(n) = O(f(n)) ,表示算法执行时间随问题规模n的增长规律。

  • n:问题规模(如数组长度、循环次数等)
  • 大O表示法:只保留最高阶项,忽略常数和低阶项
  • 语句频度f(n) = 原操作执行次数

复杂度排序(由低到高):

复制代码
O(1) < O(log n) < O(n) < O(n log n) < O(n²) < O(n³) < O(2ⁿ) < O(n!)
时间复杂度 含义 举例
O(1) 常数时间,与数据规模无关 访问数组第一个元素
O(log n) 对数时间,极其高效 二分查找
O(n) 线性时间,遍历一次 顺序查找
O(n log n) 线性对数时间 高效排序(归并、快速)
O(n²) 平方时间 冒泡排序
O(2ⁿ) 指数时间,增长极快 暴力法解NP问题

空间复杂度

空间复杂度记作 S(n) = O(f(n)),表示算法运行所需存储空间随问题规模增长的规律。

一般情况下,我们只需关注辅助存储空间(额外分配的变量、数组等),输入数据本身所占空间不计入。

排序算法综合对比

算法 平均时间复杂度 最坏时间复杂度 空间复杂度 稳定性
冒泡排序 O(n²) O(n²) O(1) 稳定
选择排序 O(n²) O(n²) O(1) 不稳定
插入排序 O(n²) O(n²) O(1) 稳定
希尔排序 O(n¹·³) O(n²) O(1) 不稳定
快速排序 O(n log n) O(n²) O(log n) 不稳定

课堂练习

用某种排序方法调整序列 7 4 2 8 1 0 6 3,最终得到 0 1 2 3 4 6 7 8,你判断使用了哪种排序算法?(提示:观察第一趟排序结果可判断)

希尔排序(设增量gap依次为5、3、1):

  • gap=5:7 4 2 8 1 0 6 3
  • gap=3:......
  • gap=1:0 1 2 3 4 6 7 8

快速排序(设Ls=52为枢轴):

  • 将Lhigh和枢轴比较,要求Lhigh ≥ 枢轴
  • 将Llow和枢轴比较,要求Llow ≤ 枢轴

第6章 自定义函数

一、知识讲解

1. 函数定义与调用

函数是一段可重复使用的代码块。使用 def 关键字定义。

python 复制代码
# 定义函数
def 函数名(参数列表):              # def 关键字 + 函数名 + 参数列表 + 冒号
    """函数说明文档(可选)"""    # 三引号包围的说明文字,描述函数功能(不是必需的但建议写)
    函数体                           # 函数要执行的代码,注意前面要有缩进(通常是 4 个空格)
    return 返回值                    # return 语句把结果返回给调用者(如果不需要返回可以省略)

# 调用函数
函数名(实参列表)                     # 通过"函数名(实际参数)"来执行函数

2. 参数传递

Python函数的参数传递方式:

  • 位置参数:按顺序传递
  • 默认参数:定义时给参数默认值,调用时可省略
  • 关键字参数 :调用时用 参数名=值 的方式传递
python 复制代码
def greet(name, greeting="你好"):  # 定义函数:name 是位置参数,greeting 有默认值 "你好"
    print(f"{greeting},{name}")   # 输出问候语,例如 "你好,张三"

greet("张三")                      # 只传 name,greeting 用默认值 "你好" → 输出"你好,张三"
greet("张三", "Hello")             # 两个参数都按位置传递 → 输出"Hello,张三"
greet(greeting="嗨", name="张三")  # 使用关键字参数,可以不按顺序传参 → 输出"嗨,张三"

3. return返回值

函数用 return 返回结果。函数遇到 return 立即结束,不再执行后续语句。

python 复制代码
def add(a, b):
    return a + b                # return 将 a+b 的结果返回给调用者
    print("这行不会执行")       # return 之后的代码永远不会被执行到

result = add(3, 5)              # 调用 add(3,5),返回值 8 被赋给 result 变量

如果没有 return,函数默认返回 None

4. 递归函数

递归是函数自己调用自己的编程技巧。递归必须包含两部分:

  • 递归终止条件:不再调用自身的情况
  • 递归调用:调用自己解决更小的问题
python 复制代码
def factorial(n):
    if n == 0:                  # 【终止条件】n = 0 时不再递归,直接返回 1
        return 1                # 0! = 1,这是递归的"出口"
    return n * factorial(n - 1) # 递归调用:n! = n × (n-1)!,函数调用自身计算更小规模的问题

递归执行过程:先递进(向下调用),再回归(逐层返回结果)。

5. lambda函数

lambda是一种简洁的匿名函数,只能写一行表达式。

python 复制代码
# 语法:lambda 参数: 表达式
f = lambda x, y: x + y          # 定义一个匿名函数,等价于 def f(x,y): return x+y
print(f(3, 5))                  # 调用 f(3,5),计算 3+5,输出 8

# 常用于排序
students = [("张三", 85), ("李四", 92), ("王五", 78)]  # 每个元素是(姓名, 成绩)元组
students.sort(key=lambda s: s[1])  # 按成绩升序排序,lambda 取出每个元组的第 2 个元素作为排序依据

6. 模块导入

将函数放在模块(.py文件)中,用 import 导入使用。

python 复制代码
# 方式1:导入整个模块
import math                     # 导入 math 模块,使用其中的函数需要加"math."前缀
print(math.sqrt(16))            # 调用 math 模块的 sqrt 函数计算平方根,√16 = 4.0

# 方式2:导入特定函数
from math import sqrt, pi       # 只导入 sqrt 函数和 pi 常量,使用时不需要加"math."
print(sqrt(16))                 # 直接调用 sqrt,输出 4.0
print(pi)                       # 直接使用 pi 常量,输出 3.141592653589793

# 方式3:导入所有(不推荐)
from math import *              # 导入 math 模块所有内容,容易引起命名冲突,不推荐使用

7. 默认参数的顺序规则(重要!)

Python语法规定:默认参数必须放在非默认参数的右侧,否则会报语法错误。

python 复制代码
# 正确写法:非默认参数在左,默认参数在右
def func(a, b=10):       # a没有默认值,b有默认值 → 正确
    return a + b

# 错误写法:默认参数放在非默认参数左边 → 语法错误!
def func(a=10, b):       # SyntaxError! a有默认值却放在b(无默认值)的左边
    return a + b

为什么有这种规定?因为Python需要根据位置来匹配参数。如果默认参数在左边,调用 func(5) 时,Python无法判断这个 5 是传给 a(覆盖默认值)还是传给 b

8. 可变参数 *args 和 **kwargs

有时候你事先不知道函数会接收多少个参数,可以使用可变参数

python 复制代码
# *param:收集剩余的所有"位置参数",打包成一个元组
def func(a, *args):
    print(f"a = {a}")         # 第一个参数正常接收
    print(f"args = {args}")   # 剩下的都收集到 args 元组中

func(1, 2, 3, 4)
# 输出:
# a = 1
# args = (2, 3, 4)         ← 多余的2,3,4被装进元组

# **param:收集剩余的所有"关键字参数",打包成一个字典
def func2(a, **kwargs):
    print(f"a = {a}")
    print(f"kwargs = {kwargs}")  # 关键字参数被收集为字典

func2(1, name="张三", age=18)
# 输出:
# a = 1
# kwargs = {'name': '张三', 'age': 18}   ← 关键字参数变成了字典

记忆技巧* 一个星号 → 元组(tuple),** 两个星号 → 字典(dict)。

9. 可变对象 vs 不可变对象的参数传递

这是考试中的常见考点。函数调用时传递参数,实际传递的是对象的引用。但不同类型的对象表现不同:

  • 不可变对象 (int、str、tuple):函数内部修改不会影响外部实参
  • 可变对象 (list、dict、set):函数内部修改会影响外部实参
python 复制代码
# 情况1:不可变对象(int)
def modify_num(n):
    n = 100                      # 修改局部变量,不会影响外部的实参
    print(f"函数内部: n = {n}")

x = 10
modify_num(x)                    # 调用后,x仍然是10
print(f"函数外部: x = {x}")
# 输出:
# 函数内部: n = 100
# 函数外部: x = 10              ← x没变!

# 情况2:可变对象(list)
def modify_list(lst):
    lst.append(4)                # 修改列表内容,会影响外部的实参!
    print(f"函数内部: lst = {lst}")

my_list = [1, 2, 3]
modify_list(my_list)
print(f"函数外部: my_list = {my_list}")
# 输出:
# 函数内部: lst = [1, 2, 3, 4]
# 函数外部: my_list = [1, 2, 3, 4]   ← 外部实参也被改变了!

为什么会有这种区别? 因为可变对象传的是"同一份数据"的引用,函数内外的变量指向的是同一个列表对象;而不变对象的"修改"实际上创建了新对象,不会影响原来的。

10. lambda在sorted排序中的高级应用

lambda常与 sorted() 配合使用,对复杂结构进行排序。

python 复制代码
# 示例1:字典按键排序
d = {'b': 2, 'a': 1, 'c': 3}
# d.items() 返回 [('b',2), ('a',1), ('c',3)],x[0]取键,x[1]取值
result = sorted(d.items(), key=lambda x: x[0])   # 按键(字母)排序
print(result)  # [('a', 1), ('b', 2), ('c', 3)]

# 示例2:字典按值排序
result = sorted(d.items(), key=lambda x: x[1])   # 按值(数字)排序
print(result)  # [('a', 1), ('b', 2), ('c', 3)]

# 示例3:列表中嵌套字典,按某个键排序
students = [
    {'name': '张三', 'age': 20},
    {'name': '李四', 'age': 18},
    {'name': '王五', 'age': 22}
]
students.sort(key=lambda x: x['age'])   # 按年龄升序排序
print(students)
# [{'name': '李四', 'age': 18}, {'name': '张三', 'age': 20}, {'name': '王五', 'age': 22}]

lambda x: x1 的含义x 代表列表中的每个元素(在这里是元组),x[1] 取出该元素下标为1的值作为排序依据。

11. global关键字

在函数内部,默认只能读取 全局变量的值,如果要对全局变量赋值修改 ,必须先用 global 声明。

python 复制代码
count = 0    # 全局变量,定义在函数外部

def increment():
    global count          # 声明:我要修改全局变量 count
    count = count + 1     # 修改全局变量,如果不声明global会报错

increment()
print(count)  # 输出:1

# 对比:如果不加 global
def reset():
    count = 0             # 这里创建了一个同名的局部变量,不是修改全局的count!
    # 全局的count不受影响

注意 :如果只是读取全局变量(如 print(count)),不需要声明 global。只有赋值操作才需要。

12. zip() 函数

zip() 像"拉链"一样,将多个可迭代对象(如列表)的对应位置元素打包成元组。

python 复制代码
# 两个列表对应位置打包
s1 = ['A', 'B', 'C']
s2 = [85, 92, 78]
result = list(zip(s1, s2))
print(result)  # [('A', 85), ('B', 92), ('C', 78)]

# 常见用法:同时遍历多个列表
names = ['张三', '李四', '王五']
scores = [90, 85, 88]
for name, score in zip(names, scores):
    print(f"{name}: {score}分")
# 输出:
# 张三: 90分
# 李四: 85分
# 王五: 88分

注意 :如果列表长度不一致,zip() 以最短的列表为准,多余的元素会被忽略。


二、例题精讲

【读程序写结果】例题1:函数调用

题目:阅读以下程序,写出运行结果。

python 复制代码
def calc(a, b):
    return a * 2 + b            # 计算 a 的两倍加上 b 并返回结果

x = 3                           # x = 3
y = 5                           # y = 5
result = calc(y, x)             # 调用 calc(y, x) 即 calc(5, 3),a=5, b=3,结果 5*2+3=13
print(result)                   # 输出结果 13

答案:13

解析 :调用 calc(y, x)calc(5, 3),函数中 a=5, b=3,计算 5*2+3=13。注意参数传递时按照位置对应,而不是按变量名。


【读程序写结果】例题2:递归函数

题目:阅读以下程序,写出运行结果。

python 复制代码
def func(n):
    if n <= 1:                  # 【终止条件】如果 n <= 1,不再递归
        return 1                # 直接返回 1
    return n + func(n - 1)      # 递归调用:计算 n + 前 n-1 个数的和

print(func(5))                  # 调用 func(5),计算 5+4+3+2+1 = 15

答案:15

解析:这是一个递归求和的过程:

  • func(5) = 5 + func(4)
  • func(4) = 4 + func(3)
  • func(3) = 3 + func(2)
  • func(2) = 2 + func(1)
  • func(1) = 1(终止条件)
  • 逐层返回:func(2)=3, func(3)=6, func(4)=10, func(5)=15

所以结果是 5+4+3+2+1 = 15。


【补全程序题】例题3:lambda补全

题目:以下程序使用lambda表达式对列表按成绩降序排序,请补全空缺。

python 复制代码
# 创建列表,用方括号 [] 包裹元素
students = [("张三", 85), ("李四", 92), ("王五", 78), ("赵六", 88)]

# 按成绩降序排序
# .sort() 原地排序(从小到大)
students.sort(key=lambda s: ________, reverse=________)

# print() 输出内容到屏幕上
print(students)

答案

  • 第1空:s[1](取每个元组的第二个元素即成绩)
  • 第2空:True(降序排序)

输出[('李四', 92), ('赵六', 88), ('张三', 85), ('王五', 78)]


【补全程序题】例题4:默认参数补全

题目:以下程序定义了一个计算商品总价的函数,请补全程序。

python 复制代码
def total_price(price, quantity=________, discount=________):
    """计算总价,quantity默认为1,discount默认为0"""
    # 计算表达式,结果赋值给 total
    total = price * quantity
    # 计算表达式,结果赋值给 total
    total = total * (________ - discount)
    return total

# 测试
print(total_price(100))            # 1件,无折扣 → 100
print(total_price(100, 3))         # 3件,无折扣 → 300
print(total_price(100, 2, 0.1))    # 2件,9折 → 180

答案

  • 第1空:1
  • 第2空:00.0
  • 第3空:11.0
  • 解析:当discount=0.1时,(1 - 0.1) = 0.9,相当于打9折。

【编程题】例题5:模块导入编程

题目:编写程序,导入math模块,计算半径为5的圆的面积和周长(圆周率使用math.pi),结果保留2位小数。

参考代码

python 复制代码
import math                     # 导入 Python 的数学模块,里面包含 pi 和 sqrt 等函数和常量

r = 5                           # 圆的半径
area = math.pi * r ** 2         # 圆的面积 = π × r²,math.pi 就是圆周率 π
circumference = 2 * math.pi * r # 圆的周长 = 2 × π × r

print(f"半径{r}的圆:")          # 打印标题,f-string 将 r 的值嵌入到字符串中
print(f"面积 = {area:.2f}")     # 输出面积,:.2f 表示保留 2 位小数
print(f"周长 = {circumference:.2f}")  # 输出周长,同样保留 2 位小数

输出

复制代码
半径5的圆:
面积 = 78.54
周长 = 31.42

【编程题】例题6:递归阶乘编程

题目 :编写程序,使用递归函数计算 n!(n的阶乘),要求输入一个正整数n,输出n!的结果。阶乘定义:n! = n × (n-1) × (n-2) × ... × 1,且0! = 1。

参考代码

python 复制代码
def factorial(n):
    if n == 0:                  # 【终止条件】0! = 1,这是递归的出口
        return 1                # 返回 1,不再继续递归
    return n * factorial(n - 1) # 递归调用:n! = n × (n-1)!

n = int(input("请输入一个正整数: "))  # 让用户输入一个正整数,int() 将字符串转为整数
result = factorial(n)           # 调用递归函数计算 n 的阶乘
print(f"{n}! = {result}")       # 输出结果,例如 "6! = 720"

示例运行

复制代码
请输入一个正整数: 6
6! = 720

三、考试提示

针对"读程序写结果"题型

  • 函数调用题:注意看清楚实参和形参的对应关系,特别是位置参数和关键字参数。
  • 递归题:画"递归调用树",从最外层向内层展开,找到终止条件后再逐层返回。建议用表格记录每层调用的参数和返回值。

针对"补全程序题"题型

  • lambda表达式常与 sort()filter()map() 结合使用。sort(key=lambda x: ...) 中的key指定排序依据。
  • 默认参数在定义时赋值,调用时可以省略。默认参数必须放在位置参数后面。
  • 注意观察上下文,推断空白处应该填写什么功能。

针对"编程题"题型

  • 递归函数必须包含终止条件,这是最重要的得分点。
  • 模块导入要记住常见模块(math、random、datetime等)的导入方式。
  • 函数定义时 def 后面要加冒号,函数体要缩进。
  • 建议先写函数定义,再写调用测试代码。

四、课后练习

练习1(读程序写结果)

题目:阅读以下程序,写出运行结果。

python 复制代码
def power(base, exp=2):         # 定义函数:base 为底数,exp 为指数(默认值为 2,即默认求平方)
    result = 1                  # 初始化结果为 1
    for i in range(exp):        # 循环 exp 次(指数是多少就乘多少次)
        result *= base          # 每次循环将 result 乘以 base(相当于累乘)
    return result               # 返回最终的计算结果

print(power(3))                 # 调用 power(3),exp 用默认值 2,计算 3² = 9
print(power(3, 3))              # 调用 power(3, 3),计算 3³ = 27

答案

复制代码
9
27

解析 :第一次调用 power(3) 使用默认参数exp=2,计算3²=9;第二次调用 power(3, 3) 传入exp=3,计算3³=27。


练习2(补全程序题)

题目:以下程序用递归函数实现字符串反转,请补全空缺。

python 复制代码
def reverse_str(s):
    # if 判断 len(s) <= ________ 是否成立
    if len(s) <= ________:
        return s
    return reverse_str(________) + ________

# print() 输出内容到屏幕上
print(reverse_str("Python"))

答案

  • 第1空:1
  • 第2空:s[1:]
  • 第3空:s[0]

解析 :当字符串长度<=1时直接返回;否则递归处理去掉首字符后的子串,再将首字符拼接到末尾。reverse_str("Python") 的递归过程:

  • reverse("Python") → reverse("ython") + "P"
  • reverse("ython") → reverse("thon") + "y"
  • ... 最终得到 "nohtyP"

练习3(编程题)

题目 :编写程序,定义一个函数 is_prime(n) 判断正整数n是否为质数,并在主程序中输出1到100之间所有的质数。

参考代码

python 复制代码
def is_prime(n):
    if n < 2:                           # 小于 2 的数(0 和 1)不是质数
        return False                    # 直接返回 False
    for i in range(2, int(n ** 0.5) + 1):  # 只需检查 2 到 √n 之间的整数
        if n % i == 0:                  # 如果 n 能被 i 整除(余数为 0)
            return False                # 说明 n 不是质数,返回 False
    return True                         # 都没有被整除,说明 n 是质数,返回 True

print("1到100之间的质数有:")           # 打印提示文字
for num in range(1, 101):               # 遍历 1 到 100 的每一个数
    if is_prime(num):                   # 调用 is_prime 判断这个数是否为质数
        print(num, end=" ")             # 如果是质数,就输出它,末尾用空格代替换行

输出

复制代码
1到100之间的质数有:
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97

五、本章模拟练习

模拟1(读程序写结果)

题目:阅读以下程序,写出运行结果。

python 复制代码
def func(a, b=2, c=3):          # 定义函数:a 是位置参数,b 默认 2,c 默认 3
    return a + b * c            # 返回 a + b × c 的计算结果

print(func(1))                  # 只传 a=1,b 和 c 用默认值 → 1 + 2×3 = 7
print(func(1, 3))               # 传 a=1, b=3,c 用默认值 3 → 1 + 3×3 = 10
print(func(1, 3, 4))            # 传 a=1, b=3, c=4 → 1 + 3×4 = 13

答案

复制代码
7
10
13

解析

  • func(1):a=1, b=2, c=3 → 1+2×3=7
  • func(1,3):a=1, b=3, c=3(默认) → 1+3×3=10
  • func(1,3,4):a=1, b=3, c=4 → 1+3×4=13

模拟2(补全程序题)

题目:以下程序使用递归函数计算两个数的最大公约数(辗转相除法),请补全空缺。

python 复制代码
def gcd(a, b):
    # if 判断 b == ________ 是否成立
    if b == ________:
        return ________
    return gcd(b, ________)

# print() 输出内容到屏幕上
print(gcd(48, 18))

答案

  • 第1空:0
  • 第2空:a
  • 第3空:a % b

解析 :辗转相除法定义:gcd(a,b) = a(当b=0),否则gcd(a,b) = gcd(b, a%b)。

计算过程:gcd(48,18) → gcd(18,12) → gcd(12,6) → gcd(6,0) → 返回6。


模拟3(编程题)

题目 :编写程序,定义一个函数 avg_score(scores),接收一个成绩列表,计算并返回平均分(去掉一个最高分和一个最低分后的平均值)。主程序测试 [85, 92, 78, 90, 88, 95]

参考代码

python 复制代码
def avg_score(scores):
    if len(scores) <= 2:                    # 如果成绩个数不超过 2 个
        return sum(scores) / len(scores)    # 直接返回平均值(无法再去掉最高最低)
    sorted_scores = sorted(scores)          # 将成绩从小到大排序
    trimmed = sorted_scores[1:-1]           # 切片去掉第一个(最低分)和最后一个(最高分)
    return sum(trimmed) / len(trimmed)      # 返回剩余成绩的平均值

scores = [85, 92, 78, 90, 88, 95]          # 测试用的成绩列表
result = avg_score(scores)                  # 调用函数计算去掉最高最低后的平均分
print(f"去掉最高最低分后的平均分: {result:.2f}")  # 输出结果,保留 2 位小数

输出去掉最高最低分后的平均分: 88.75

计算过程:排序后 78, 85, 88, 90, 92, 95,去掉78和95,剩余 85, 88, 90, 92,平均 (85+88+90+92)/4 = 88.75。


第7章 文件操作

一、知识讲解

1. 打开文件:open()函数

python 复制代码
# open() 是Python内置的打开文件的函数
# 第一个参数是文件名,第二个参数是打开模式,encoding指定文件编码
file = open(文件名, 模式, encoding='编码')

# 常用模式说明:
# 'r'  - 只读模式(默认值),如果文件不存在会报错(FileNotFoundError)
# 'w'  - 写入模式,文件不存在则自动创建新文件,文件存在则覆盖原有内容
# 'a'  - 追加模式(append),文件不存在则自动创建,存在则在文件末尾追加内容
# 'rb' - 二进制只读模式,用于读取图片、视频等非文本文件
# 'wb' - 二进制写入模式,用于写入图片、视频等非文本文件

# 使用 open() 打开文件后,一定要记得调用 close() 关闭文件
# 否则文件资源可能被长时间占用,其他程序可能无法访问该文件
file.close()

2. with语句(推荐)

使用 with 打开文件可以自动关闭文件,不需要手动调用 close()

python 复制代码
# with 语句是Python推荐的打开文件方式
# 用 with 打开文件的最大好处是:不需要手动调用 close()
# 程序执行完 with 内部的代码块后,文件会被自动关闭
with open('file.txt', 'r', encoding='utf-8') as f:
    # f.read() 一次性读取文件的全部内容,返回一个字符串
    content = f.read()
# 执行到这里时,文件已经自动关闭了,无需写 f.close()

3. 读取文件

python 复制代码
# 方法1:read() - 一次性读取文件的全部内容(返回一个字符串)
# 计算表达式,结果赋值给 with open('file.txt', 'r', encoding
with open('file.txt', 'r', encoding='utf-8') as f:
    # read() 会把整个文件内容当作一个字符串返回,包括其中的换行符
    content = f.read()

# 方法2:readline() - 逐行读取,每次只读取一行
# 计算表达式,结果赋值给 with open('file.txt', 'r', encoding
with open('file.txt', 'r', encoding='utf-8') as f:
    line = f.readline()  # 先读取第一行(返回字符串,包含行尾的换行符)
    while line:          # 如果 line 不是空字符串,说明还没读完,继续循环
        print(line, end='')  # 打印当前行,end='' 表示不额外换行(因为line本身已带换行符)
        line = f.readline()  # 继续读取下一行

# 方法3:readlines() - 一次性读取所有行,返回一个列表
# 计算表达式,结果赋值给 with open('file.txt', 'r', encoding
with open('file.txt', 'r', encoding='utf-8') as f:
    # 文件的每一行作为列表中的一个元素,每行末尾包含换行符
    lines = f.readlines()  # 例如:['第一行\n', '第二行\n', '第三行\n']

4. 写入文件

python 复制代码
# 'w' 模式是覆盖写入:如果文件已存在,会清空原内容重新写入
# 计算表达式,结果赋值给 with open('file.txt', 'w', encoding
with open('file.txt', 'w', encoding='utf-8') as f:
    f.write('Hello, World!\n')  # write() 写入字符串,注意:不会自动换行,需要手动写 \n
    f.write('第二行内容\n')      # 再次写入,会在第一行后面接着写

# 'a' 模式是追加写入:在文件末尾添加新内容,不会影响原内容
# 计算表达式,结果赋值给 with open('file.txt', 'a', encoding
with open('file.txt', 'a', encoding='utf-8') as f:
    f.write('追加的内容\n')      # 新内容会被添加到文件的末尾

5. CSV文件读写

CSV(逗号分隔值)是一种常见的数据格式。

python 复制代码
# 写入CSV
# 导入 csv 库
import csv
with open('data.csv', 'w', newline='', encoding='utf-8') as f:
    writer = csv.writer(f)
    writer.writerow(['姓名', '年龄', '成绩'])  # 写入表头
    writer.writerow(['张三', 18, 92])
    writer.writerow(['李四', 19, 85])

# 读取CSV
# 导入 csv 库
import csv
# 计算表达式,结果赋值给 with open('data.csv', 'r', encoding
with open('data.csv', 'r', encoding='utf-8') as f:
    reader = csv.reader(f)
    # for 循环:遍历 reader 中的每个元素
    for row in reader:
        print(row)  # row是一个列表

6. 文件模式比较

模式 文件存在 文件不存在 写入位置 是否清空原内容
'r' 正常读取 报错 - -
'w' 覆盖写入 新建 文件开头
'a' 追加写入 新建 文件末尾

7. open()打开模式的完整表格

模式 说明 文件类型 操作
'r' 只读(默认) 文本 文件必须存在,否则报错
'w' 覆盖写入 文本 文件不存在则新建,存在则清空后写入
'a' 追加写入 文本 文件不存在则新建,存在则在末尾追加
'r+' 读写 文本 文件必须存在,可读可写(不截断)
'w+' 读写 文本 文件不存在则新建,存在则清空后读写
'a+' 读写追加 文本 文件不存在则新建,写入从末尾开始
'rb' 二进制只读 二进制 用于读取图片、视频、音频等非文本文件
'wb' 二进制写入 二进制 用于写入图片、视频、音频等非文本文件

8. 绝对路径 vs 相对路径

文件路径是告诉计算机"文件在哪里"的地址,分为两种:

  • 绝对路径 :从盘符开始的完整路径。例如 C:\Users\zhang\Desktop\data.txt。无论当前程序在哪个目录运行,绝对路径都指向同一个文件。
  • 相对路径 :从当前工作目录开始的路径。例如 data.txt(当前目录下的文件)、.\data\file.txt(当前目录data子文件夹下的文件)、..\file.txt(上级目录下的文件)。
python 复制代码
# 绝对路径:以盘符开头
with open('C:\\Users\\zhang\\Desktop\\data.txt', 'r', encoding='utf-8') as f:
    content = f.read()

# 相对路径:从当前目录开始
with open('data.txt', 'r', encoding='utf-8') as f:       # 当前目录下的data.txt
    content = f.read()
with open('.\\data\\file.txt', 'r', encoding='utf-8') as f:  # 当前目录下data文件夹中的file.txt
    content = f.read()

考试中一般使用相对路径,因为考试环境中的文件路径是固定的。如果题目没有指定路径,默认文件就在当前目录下。

9. write() vs writelines() 的区别

很多同学容易混淆这两个方法,它们的区别如下:

python 复制代码
# write():写入一个字符串
with open('test.txt', 'w', encoding='utf-8') as f:
    f.write('Hello')           # 写一个字符串
    f.write('World')           # 再写一个字符串,不会自动换行
# 文件内容:HelloWorld

# writelines():写入一个字符串列表(每个元素作为一行)
lines = ['第一行\n', '第二行\n', '第三行\n']
with open('test.txt', 'w', encoding='utf-8') as f:
    f.writelines(lines)        # 把列表中的每个字符串依次写入
# 文件内容:
# 第一行
# 第二行
# 第三行

关键区别

  • write(字符串) ------ 参数是一个字符串
  • writelines(列表) ------ 参数是一个字符串列表(或任何可迭代对象)
  • 两者都不会自动添加换行符 ,需要手动在字符串末尾加 \n

10. CSV写入中的 newline='' 参数

在Windows系统上写入CSV文件时,如果不在 open() 中指定 newline='',写入的每一行之间会多出空行。

python 复制代码
import csv

# 错误写法:不指定newline,在Windows上会产生多余空行
with open('data.csv', 'w', encoding='utf-8') as f:
    writer = csv.writer(f)
    writer.writerow(['姓名', '年龄'])
    writer.writerow(['张三', 18])
# 结果:每行之间会多一个空行(Windows上换行符被重复处理)

# 正确写法:指定 newline='',避免多余空行
with open('data.csv', 'w', newline='', encoding='utf-8') as f:
    writer = csv.writer(f)
    writer.writerow(['姓名', '年龄'])
    writer.writerow(['张三', 18])
# 结果:正常,没有多余空行

原因 :Windows系统默认的换行符是 \r\n,而CSV的 writer 也会写入 \r\n,如果不指定 newline='',两者叠加就会产生 \r\r\n,导致多出一个空行。newline='' 告诉Python不要自动转换换行符,让CSV模块自己处理。

11. 常见异常类型

写程序时难免会出错,Python用"异常"(Exception)来告诉你出了什么错。考试中常见的异常类型:

异常类型 含义 触发场景举例
ZeroDivisionError 除以零错误 10 / 0 ------ 数学上不允许除以零
IOError 输入/输出错误 尝试打开一个不存在的文件
IndexError 索引越界错误 lst = [1,2,3]; lst[5] ------ 访问了不存在的下标
KeyError 键不存在错误 d = {'a':1}; d['b'] ------ 字典中没有这个键
NameError 变量名不存在错误 print(x) ------ 变量x还没有定义

12. try-except-else 异常处理结构

程序遇到异常会直接崩溃,但我们可以用 try-except 来"捕获"异常,让程序优雅地处理错误。

python 复制代码
# 基本结构
try:
    # 尝试执行的代码(可能出错的代码)
    num = int(input("请输入一个数字: "))
    result = 10 / num
except ZeroDivisionError:
    # 如果 try 中发生了 ZeroDivisionError,就执行这里的代码
    print("错误:不能除以 0!")
except ValueError:
    # 如果 try 中发生了 ValueError(比如输入的不是数字),执行这里
    print("错误:请输入有效的数字!")
else:
    # 如果 try 中没有任何异常发生,执行这里的代码
    print(f"计算结果: {result}")

三个部分的执行逻辑

  • try 块:先执行,如果不出错就继续
  • except 块:只有当 try 中发生了对应类型的异常时才执行
  • else 块:只有当 try完全没有异常时才执行(相当于"一切顺利"的后续操作)

注意else 是可选的,不是必须写的。try-except 也可以只写 try-except 而不写 else


二、例题精讲

【读程序写结果】例题1:read() 读程序

题目 :假设当前目录下有一个名为 data.txt 的文件,内容如下:

复制代码
Python
Java
C++

阅读以下程序,写出运行结果。

python 复制代码
with open('data.txt', 'r', encoding='utf-8') as f:  # 用只读模式打开文件 data.txt,编码为utf-8
    content = f.read()          # read() 一次性读取文件的全部内容,包括所有换行符
    print(repr(content))        # repr() 显示字符串的"官方表示形式",可以看到 \n 等转义字符

答案'Python\nJava\nC++\n'

复制代码
Python
Java
C++

解析read() 一次性读取文件的全部内容,包括换行符。repr() 函数可以显示字符串中的转义字符。最后一行末尾也有换行符,所以结果是 'Python\nJava\nC++\n'


【读程序写结果】例题2:模式比较

题目:阅读以下程序,写出运行结果。

python 复制代码
# 假设当前b.txt内容为:"Hello"

with open('b.txt', 'w', encoding='utf-8') as f:  # 以'w'(覆盖写入)模式打开b.txt
    f.write('World')            # 写入"World",由于是'w'模式,原内容"Hello"被完全覆盖

with open('b.txt', 'r', encoding='utf-8') as f:  # 以'r'(只读)模式重新打开b.txt
    print(f.read())             # 读取文件内容并打印 → 输出:World(原Hello已被覆盖)

答案World

解析 :用 'w' 模式打开文件时,会覆盖原有内容。原文件内容是"Hello",以'w'模式写入"World"后,原内容被完全覆盖。所以读取时只读到"World"。


【补全程序题】例题3:with语句补全

题目:以下程序将列表中的内容逐行写入文件,请补全空缺。

python 复制代码
fruits = ["苹果", "香蕉", "橙子", "葡萄"]  # 定义一个水果列表,包含4种水果名称

________ open('fruits.txt', 'w', encoding='utf-8') as f:  # 【填空】用with语句打开文件
    for fruit in ________:       # 【填空】遍历fruits列表中的每个水果
        ________(fruit + '\n')   # 【填空】将水果名称加上换行符后写入文件

# 验证写入结果
with open('fruits.txt', 'r', encoding='utf-8') as f:  # 重新以只读模式打开,验证写入内容
    print(f.read())              # 读取并打印文件全部内容

答案

  • 第1空:with
  • 第2空:fruits
  • 第3空:f.writef.write

【补全程序题】例题4:CSV读写补全

题目:以下程序将学生成绩写入CSV文件,再读取并计算平均分,请补全。

python 复制代码
import csv                    # 导入csv模块,用于处理CSV格式文件

# 写入数据
students = [                   # 定义一个嵌套列表,每个子列表包含姓名和成绩
    ['张三', 85],
    ['李四', 92],
    ['王五', 78]
]

with open('scores.csv', 'w', newline='', encoding='utf-8') as f:  # 以覆盖写入模式打开CSV文件
    writer = csv.________(f)   # 【填空】创建CSV写入器对象
    writer.writerow(['姓名', '成绩'])  # 先写入表头行:第一列"姓名",第二列"成绩"
    for s in students:         # 遍历每个学生的数据
        writer.________(s)     # 【填空】将当前学生的数据作为一行写入CSV

# 读取并计算平均分
total = 0                      # 用于累加所有学生的成绩总分
count = 0                      # 用于统计学生人数
with open('scores.csv', 'r', encoding='utf-8') as f:  # 以只读模式打开CSV文件
    reader = csv.________(f)   # 【填空】创建CSV读取器对象
    next(reader)               # next() 跳过第一行(表头),从第二行开始才是真正的数据
    for row in reader:         # 遍历数据行,每行是一个列表,如 ['张三', '85']
        total += int(row[________])  # 【填空】将成绩(第2列,下标为1)转为整数并累加
        count += 1             # 学生人数加1

avg = total / count            # 总分 ÷ 人数 = 平均分
print(f"平均分: {avg:.1f}")    # 打印平均分,保留1位小数

答案

  • 第1空:writer
  • 第2空:writerow
  • 第3空:reader
  • 第4空:1(成绩在第2列,下标为1)

【编程题】例题5:文件统计编程

题目 :假设文件 article.txt 内容如下:

复制代码
Hello Python
Python is great
I love Python

编写程序,读取该文件内容,统计:

  1. 文件的总行数
  2. 文件中单词 "Python" 出现的次数
  3. 将统计结果写入文件 result.txt

参考代码

python 复制代码
# 统计
line_count = 0     # 记录文件的总行数,初始为0
word_count = 0     # 记录单词"Python"出现的次数,初始为0

with open('article.txt', 'r', encoding='utf-8') as f:  # 以只读模式打开文章文件
    for line in f:       # 逐行遍历文件(这是Python推荐的逐行读取方式,内存友好)
        line_count += 1  # 每读取一行,行数计数器加1
        word_count += line.count('Python')  # 统计当前行中"Python"出现的次数,累加到总次数

# 写入结果
with open('result.txt', 'w', encoding='utf-8') as f:  # 以覆盖写入模式打开结果文件
    f.write(f"总行数: {line_count}\n")       # 写入统计结果:总行数
    f.write(f"Python出现次数: {word_count}\n")  # 写入统计结果:Python出现次数

print("统计完成,结果已写入result.txt")  # 在屏幕上显示提示信息

输出(result.txt内容)

复制代码
总行数: 3
Python出现次数: 3

三、考试提示

针对"读程序写结果"题型

  • read() 返回整个字符串(含换行符);readline() 只读一行;readlines() 返回字符串列表。
  • 注意 'w' 模式会覆盖 文件,'a' 模式会追加 。未指定模式时默认为 'r'
  • 注意编码问题:windows系统默认编码可能不是utf-8,考试中通常会指定。

针对"补全程序题"题型

  • with open(...) as 变量名: 是固定写法,常考关键字 withas
  • CSV操作三步:csv.writer(f)csv.reader(f) → 调用 writerow() 或遍历 reader
  • 文件写入时要手动加 \n 换行,write() 不会自动换行。
  • next(reader) 用于跳过CSV表头行。

针对"编程题"题型

  • 文件统计类题目:常用 for line in f: 逐行遍历。
  • 写入文件时注意模式选择('w'覆盖 vs 'a'追加)。
  • 使用 with 语句是加分项,体现了代码规范性。
  • 文件路径中不要忘了编码参数 encoding='utf-8'

四、课后练习

练习1(读程序写结果)

题目 :假设当前目录下 score.txt 内容为:

复制代码
88
92
75

阅读以下程序,写出运行结果。

python 复制代码
total = 0                      # 用于累加所有分数的总和,初始为0
count = 0                      # 用于统计有多少个分数,初始为0
with open('score.txt', 'r', encoding='utf-8') as f:  # 以只读模式打开分数文件
    for line in f:             # 逐行读取文件内容
        total += int(line.strip())  # strip()去掉行尾换行符,int()转为整数,然后累加到total
        count += 1             # 每读取一行,计数器加1
print(total / count)           # 总分 ÷ 人数 = 平均分,打印结果

答案85.0

解析 :程序逐行读取文件内容,将每行字符串转为整数后累加。88+92+75=255,255÷3=85.0。strip() 去除每行末尾的换行符。


练习2(补全程序题)

题目:以下程序实现文件复制功能(将source.txt的内容复制到target.txt),请补全。

python 复制代码
with ________('source.txt', 'r', encoding='utf-8') as f1:  # 【填空】用open打开源文件(只读模式)
    content = ________         # 【填空】用read()读取源文件的全部内容

with open('target.txt', 'w', encoding='utf-8') as f2:      # 以覆盖写入模式打开目标文件
    ________                   # 【填空】将读取到的内容写入目标文件

print("文件复制完成")          # 提示用户操作完成

答案

  • 第1空:open
  • 第2空:f1.read()
  • 第3空:f2.write(content)

练习3(编程题)

题目 :编写程序,接收用户输入的一行文本,将其写入文件 note.txt(追加模式),然后读取该文件的所有内容并显示在屏幕上。

参考代码

python 复制代码
text = input("请输入一行文本: ")  # 让用户输入一行文本,存入变量text

# 追加写入
with open('note.txt', 'a', encoding='utf-8') as f:  # 'a'模式:在文件末尾追加内容,不影响原内容
    f.write(text + '\n')         # 将用户输入的文本写入文件,后面加换行符

# 读取全部内容
with open('note.txt', 'r', encoding='utf-8') as f:  # 以只读模式打开文件,查看完整内容
    content = f.read()            # 一次性读取所有内容,包括之前写入的所有行

print("文件当前内容:")            # 打印提示信息
print(content)                   # 打印文件的全部内容

五、本章模拟练习

模拟1(读程序写结果)

题目 :假设当前目录下 names.txt 内容为:

复制代码
Alice
Bob
Charlie
David

阅读以下程序,写出运行结果。

python 复制代码
with open('names.txt', 'r', encoding='utf-8') as f:  # 以只读模式打开names.txt文件
    lines = f.readlines()        # readlines() 读取所有行,返回一个列表,每行是一个元素

count = len(lines)               # len() 获取列表长度,即文件的总行数
print(f"总共有 {count} 行")      # 使用f-string格式化输出总行数
print(f"第2行是: {lines[1].strip()}")  # lines[1]是第2行(列表下标从0开始),strip()去掉换行符

答案

复制代码
总共有 4 行
第2行是: Bob

模拟2(补全程序题)

题目 :以下程序从 data.txt 中读取数字,计算其中正数的和并输出,请补全。

python 复制代码
# data.txt 内容:-5 12 -3 8 0 -1 15 7
total = 0                        # 用于累加正数的和,初始为0

with open('data.txt', 'r', encoding='utf-8') as f:  # 以只读模式打开数据文件
    content = f.read()           # read() 一次性读取全部内容,得到一个字符串
    numbers = ________.split()   # 【填空】用split()将字符串按空格拆分成字符串列表
    for num_str in numbers:      # 遍历列表中的每个数字字符串
        num = int(________)      # 【填空】将当前数字字符串转为整数
        if num ________ 0:       # 【填空】判断是否为正数(大于0)
            total += num         # 如果是正数,累加到total

print(f"正数之和为: {total}")    # 打印所有正数的和

答案

  • 第1空:content
  • 第2空:num_str
  • 第3空:>

模拟3(编程题)

题目 :编写程序,生成一个包含10个1~100之间随机整数的文件 random.txt,每行一个数。然后再读取该文件,找出其中的最大值和最小值。

参考代码

python 复制代码
import random                  # 导入random模块,用于生成随机数

# 生成并写入随机数
with open('random.txt', 'w', encoding='utf-8') as f:  # 以覆盖写入模式打开文件
    for i in range(10):        # 循环10次,生成10个随机数
        num = random.randint(1, 100)  # randint(1,100) 生成1到100之间的随机整数
        f.write(str(num) + '\n')     # 将数字转为字符串后写入文件,每行一个数

# 读取并找最大最小值
with open('random.txt', 'r', encoding='utf-8') as f:  # 以只读模式打开文件
    nums = []                  # 创建一个空列表,用于存储所有随机数
    for line in f:             # 逐行读取文件
        nums.append(int(line.strip()))  # 去掉换行符,转为整数,添加到列表末尾

print(f"生成的随机数: {nums}")       # 打印所有随机数
print(f"最大值: {max(nums)}")        # max() 找出列表中的最大值
print(f"最小值: {min(nums)}")        # min() 找出列表中的最小值