一、流程控制
1)条件分支
1.if / elif / else
python
x = 10
if x > 20:
print("A")
elif x > 5:
print("B")
else:
print("C")
2.三元表达式
python
result = "big" if x > 5 else "small"
2)循环结构
1.for 循环 ------ 专用于遍历可迭代对象
python
for i in [1, 2, 3]: # 列表
print(i)
for ch in "hello": # 字符串
print(ch)
# range(start, stop, step) 生成整数序列
for i in range(5): # 0 1 2 3 4
print(i)
for i in range(2, 6): # 2 3 4 5
print(i)
for i in range(10, 0, -2): # 10 8 6 4 2
print(i)
for idx, val in enumerate(["a", "b", "c"]):
print(idx, val) # 0 a, 1 b, 2 c
2.while循环
python
count = 0
while count < 5:
print(count)
count += 1
3.break / continue / else
-
break跳出整个循环 -
continue跳过本次循环剩余代码,进入下一次迭代 -
else子句:如果循环正常结束 (没有被break中断),会执行else块
python
for i in range(5):
if i == 3:
break
else:
print("没 break") # 这里不会执行,因为 break 了
3)流程控制练习题
题目1:条件分支 ------ 成绩等级转换
从键盘输入一个分数(0--100 的整数),根据规则打印等级:
-
90 及以上:A
-
80--89:B
-
70--79:C
-
60--69:D
-
60 以下:F
输入不是数字或越界则提示错误。
python
try:
score = int(input("请输入一个分数:"))
if score >=0 and score <=100:
if score >= 90:
print("分数等级为A")
elif score >=80:
print("分数等级为B")
elif score >=70:
print("分数等级为C")
elif score >= 60:
print("分数等级为D")
else:
print("分数等级为F")
else:
print("分数不在0~100范围内")
except ValueError:
print("非数字输入错误")
题目2:for 循环 ------ 统计与列表生成
给定列表 numbers = [3, 7, 12, 5, 8, 11, 6],请:
-
用
for循环计算所有元素的和,并找出最大值、最小值(不要用内置sum/max/min,自己写循环) -
生成一个新列表,包含所有偶数的平方(用列表推导式)
python
numbers = [3, 7, 12, 5, 8, 11, 6]
numbers_sum = 0
numbers_max = -9999999
numbers_min = 9999999
for x in numbers:
numbers_sum += x
numbers_max = x if numbers_max < x else numbers_max
numbers_min = x if numbers_min > x else numbers_min
numbers_square = [y*y for y in numbers if y%2 == 0]
print(f"元素总和为{numbers_sum},最大值为{numbers_max},最小值为{numbers_min},偶数平方列表为{numbers_square}")
运行结果:
python
元素总和为52,最大值为12,最小值为3,偶数平方列表为[144, 64, 36]
题目3:嵌套循环与 break/else ------ 查找质数
打印 2 到 20 之间的所有质数。要求:
-
外层循环遍历数字,内层循环检查是否存在因子
-
正确使用
for-else结构,在未找到因子时打印该数
python
for x in range(2,21):
for n in range(2,x):
if x%n == 0:
break
else:
print(x, end=" ")
运行结果:
python
2 3 5 7 11 13 17 19
题目4:while 循环 ------ 猜数字游戏
程序随机生成一个 1 到 100 之间的整数,用户每次输入猜测,给出"大了"、"小了"提示,直到猜对为止,并统计猜测次数。
要求用 while True + break 实现。
python
num = random.randint(1,100)
cs = 0
while True:
try:
x = int(input("请输入一个1~100之间的整数(包括1和100):"))
cs +=1
if x > num:
print("大了")
elif x < num:
print("小了")
else:
print(f"恭喜你猜中了!幸运数为{num}")
break
except ValueError:
print("您输入的不是整数,请重新输入")
运行结果:
python
请输入一个1~100之间的整数(包括1和100):50
小了
请输入一个1~100之间的整数(包括1和100):75
大了
请输入一个1~100之间的整数(包括1和100):62
小了
请输入一个1~100之间的整数(包括1和100):69
小了
请输入一个1~100之间的整数(包括1和100):72
大了
请输入一个1~100之间的整数(包括1和100):70
恭喜你猜中了!幸运数为70
题目5:综合 ------ 菜单驱动程序骨架
实现一个简单的菜单循环:
1. Say Hello 2. Count numbers 3. Exit
选择 1 时打印 "Hello!",选择 2 时让用户输入一个正整数 N,然后用 for 打印 1 到 N,选择 3 退出,其他输入提示无效。
要求使用 while 循环保持菜单运行,直到退出。
python
while True:
print("以下为选项菜单,请输入数字1、2或3")
print("1. Say Hello")
print("2. Count numbers")
print("3. Exit")
try:
num = int(input("请输入:"))
if num == 1:
print("Hello!")
elif num == 2:
while True:
try:
N = int(input("请输入一个正整数N:"))
for x in range(1,N):
print(x,end= " ")
else:
print(N)
break
except ValueError:
print("输入的N不是数字,请重新输入")
elif num == 3:
print("退出菜单")
break
else:
print("输入的不是数值1、2或3,请重新输入")
except ValueError:
print("您输入菜单选项的不是数字,请重新输入")
运行结果:
python
以下为选项菜单,请输入数字1、2或3
1. Say Hello
2. Count numbers
3. Exit
请输入:a
您输入菜单选项的不是数字,请重新输入
以下为选项菜单,请输入数字1、2或3
1. Say Hello
2. Count numbers
3. Exit
请输入:0
输入的不是数值1、2或3,请重新输入
以下为选项菜单,请输入数字1、2或3
1. Say Hello
2. Count numbers
3. Exit
请输入:1
Hello!
以下为选项菜单,请输入数字1、2或3
1. Say Hello
2. Count numbers
3. Exit
请输入:2
请输入一个正整数N:5
1 2 3 4 5
以下为选项菜单,请输入数字1、2或3
1. Say Hello
2. Count numbers
3. Exit
请输入:3
退出菜单
二、函数
1)基本定义 ------ 没有类型声明,全靠 def
python
def add(a, b):
return a + b
2)参数传递 ------ 记住两句口诀
-
可变对象(list/dict/set)在函数内修改会影响到外部。
-
不可变对象(int/str/tuple)不会影响外部,因为"修改"实际上是重新绑定。
python
def try_change(num, lst):
num = 999 # 新标签,不影响外部
lst.append(4) # 直接修改原对象,外部可见
a = 10
b = [1,2,3]
try_change(a, b)
print(a, b) # 10, [1,2,3,4]
3)参数形态 ------ 灵活多变
python
# 默认参数(默认值只初始化一次,不要用可变对象当默认值!)
def greet(name, msg="Hello"):
return f"{msg}, {name}"
# 位置参数、关键字参数
def config(host, port=80, timeout=30):
pass
config("1.2.3.4", timeout=10) # 关键字传参
config(port=443, host="localhost") # 打乱顺序也可以
# 可变参数(*args)------ 接收任意数量位置参数,打包成元组
def sum_all(*numbers):
total = 0
for n in numbers:
total += n
return total
print(sum_all(1, 2, 3, 4)) # 10
# 关键字可变参数(**kwargs)------ 接收任意数量关键字参数,打包成字典
def print_info(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
print_info(name="Tom", age=20)
# 解包实参
nums = [1, 2, 3]
print(sum_all(*nums)) # 列表解开传给 *args
info = {"name": "Tom", "age": 20}
print_info(**info) # 字典解开传给 **kwargs
4)返回值 ------ 天生支持"多返回值"
python
def min_max(a, b):
if a > b:
return b, a # 返回的是元组 (b, a)
return a, b
small, big = min_max(5, 3) # 自动解包
5)lambda 表达式 ------ 匿名函数
python
add = lambda x, y: x + y
print(add(2, 3)) # 5
# 常用于排序、filter、map
pairs = [(1, 'one'), (3, 'three'), (2, 'two')]
pairs.sort(key=lambda p: p[0]) # 按第一个元素排序
6)装饰器 ------ 给函数"穿衣服"
python
def my_decorator(func):
def wrapper():
print("Before")
func()
print("After")
return wrapper
@my_decorator
def say_hi():
print("Hi!")
say_hi()
# 输出:Before / Hi! / After
7)作用域规则 ------ LEGB
查找顺序:L ocal → E nclosing function → G lobal → Built-in
-
在函数内要修改全局变量,需要
global声明。 -
闭包内修改外层变量,需要
nonlocal。
8)函数练习题
题目1:基础定义与返回值
写一个函数 rectangle_info(length, width),返回一个元组,包含矩形的面积和周长。
调用函数并解包打印结果。
python
def rectangle_info(length, width):
return length * width, (length + width) * 2
s, c = rectangle_info(2, 3)
print(f"边长为2,3的举行的面积为{s},周长为{c}")
运行结果:
python
边长为2,3的举行的面积为6,周长为10
题目2:列表参数与原地修改
写一个函数 remove_duplicates(lst),接受一个列表,原地 移除重复元素(保持原有顺序),不返回新列表。
思考:如果要求生成一个新列表并返回,该怎么做?
python
def remove_duplicates(lst):
i = 0
while i < len(lst):
j = i + 1
while j < len(lst):
if(lst[j] == lst[i]):
lst.pop(j)
else:
j += 1
i += 1
li = [1, 3, 3, 5, 7, 3, 5, 9]
remove_duplicates(li)
print(f"去重后的列表为{li}")
# 思考:如果要求生成一个新列表并返回,该怎么做?
def remove_duplicates_new_list(lst):
lst_tmp = []
i = 0
while i < len(lst):
if lst[i] not in lst_tmp:
lst_tmp.append(lst[i])
i += 1
return lst_tmp
li_old = [1, 3, 3, 5, 7, 3, 5, 9]
li_new = remove_duplicates_new_list(li_old)
print(f"老列表为:{li_old},新列表为{li_new}")
运行结果:
python
去重后的列表为[1, 3, 5, 7, 9]
老列表为:[1, 3, 3, 5, 7, 3, 5, 9],新列表为[1, 3, 5, 7, 9]
题目3:递归 ------ 斐波那契与搜索
(1)用递归实现斐波那契数列:fib(n) 返回第 n 项(从 0 开始:fib(0)=0, fib(1)=1)。
(2)写一个递归函数 binary_search(arr, target, low, high) 在有序列表中查找目标,找到返回索引,否则返回 -1。
python
def fib(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return fib(n - 2) + fib(n - 1)
print(f"斐波那契的第8项为{fib(8)}") # 0 1 1 2 3 5 8 13 21
def binary_search(arr, target, low, high):
mid = (low + high) // 2
if low > high:
return -1
elif target == arr[mid]:
return mid
elif target < arr[mid]:
return binary_search(arr, target, low, mid - 1)
elif target > arr[mid]:
return binary_search(arr, target, mid + 1, high)
sorted_list = [2, 5, 8, 12, 16, 23, 38]
print(f"16对应的项为第{binary_search(sorted_list, 16, 0, len(sorted_list)-1)}项") # 4
运行结果:
python
斐波那契的第8项为21
16对应的项为第4项
题目4:可变参数与关键字参数
写一个函数 create_profile(name, **details),要求:
-
name是必须的位置参数 -
details接收任意关键字参数(如age=25, city="NY") -
返回一个字典,包含 name 和所有 details
-
调用时用关键字传入任意额外信息
python
def create_profile(name, **details):
info = {}
info["姓名"] = name
info.update(details)
return info
xiaoming_info = create_profile("小明",年龄 = 25, 性别 = "男", 城市 = "杭州")
print(f"小明的相关信息:{xiaoming_info}")
运行结果:
python
小明的相关信息:{'姓名': '小明', '年龄': 25, '性别': '男', '城市': '杭州'}
题目5:lambda 与高阶函数
给定列表 students = [("Tom", 85), ("Jerry", 92), ("Spike", 78)]:
-
用
sorted()按分数降序排列(用lambda指定 key) -
用
filter()结合lambda筛选分数 ≥ 80 的学生 -
用
map()结合lambda将所有姓名转为大写
python
students = [("Tom", 85), ("Jerry", 92), ("Spike", 78)]
students_sort = sorted(students, key = lambda p:p[1], reverse=True)
print(f"分数降序后的成绩列表为:{students_sort}")
students_more80 = list(filter(lambda p:p[1] >= 80,students))
print(f"分数>=80的成绩列表为:{students_more80}")
students_upper = list(map(lambda p:p[0].upper(),students))
print(f"姓名转成大写后的列表为:{students_upper}")
运行结果:
python
分数降序后的成绩列表为:[('Jerry', 92), ('Tom', 85), ('Spike', 78)]
分数>=80的成绩列表为:[('Tom', 85), ('Jerry', 92)]
姓名转成大写后的列表为:['TOM', 'JERRY', 'SPIKE']
三、类和继承
1)定义一个类 ------ 没有头文件,没有类型声明
python
class Dog:
# 类属性(所有实例共享,类似静态成员)
species = "Canis familiaris"
# 构造方法(初始化实例),第一个参数固定是 self
def __init__(self, name, age):
self.name = name # 实例属性
self.age = age
# 实例方法
def bark(self):
print(f"{self.name} says woof!")
2)创建对象、访问属性
python
my_dog = Dog("Fido", 3)
print(my_dog.name) # Fido
my_dog.bark() # Fido says woof!
my_dog.color = "brown" # 可以随意在运行时添加属性
3)继承 ------ 重用与多态
python
class Beagle(Dog): # 继承自 Dog
def bark(self): # 重写方法
print(f"{self.name} howls!")
class Cat:
def __init__(self, name):
self.name = name
def speak(self):
print(f"{self.name} meows")
python
class Beagle(Dog):
def __init__(self, name, age, trick):
super().__init__(name, age) # 调用父类 __init__
self.trick = trick
4)特殊方法 ------ "魔术方法"
python
以双下划线包裹,定制类的行为:
__str__(self):被 print() 或 str() 调用,返回用户友好的字符串。
__repr__(self):调试用的"官方"表示,通常返回能重建对象的字符串。
__eq__(self, other):定义 == 行为。
__len__、__getitem__ 等让自定义类像内置容器。
5)类与继承练习题
题目1:定义一个矩形类
创建一个 Rectangle 类:
-
初始化传入
width和height -
提供
area()方法返回面积 -
提供
perimeter()方法返回周长 -
重写
__str__方法,返回形如"Rectangle(width, height)"的字符串
python
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
def __str__(self):
return f"Rectangle({self.width}, {self.height})"
def area(self):
return self.width * self.height
def perimeter(self):
return (self.width + self.height) * 2
rect = Rectangle(2, 3)
print(rect)
print(f"边长为2和3的长方形面积为:{rect.area()},周长为{rect.perimeter()}")
运行结果:
python
Rectangle(2, 3)
边长为2和3的长方形面积为:6,周长为10
题目2:继承与方法重写
设计一个类层次:
-
Vehicle基类,初始化brand,model;提供get_description()返回"brand model" -
Car子类,增加num_doors属性;重写get_description()返回"brand model (Car, num_doors doors)",并在其中调用父类的get_description() -
Motorcycle子类,增加has_sidecar布尔属性;重写get_description(),展示是否有侧斗
python
class Vehicle:
def __init__(self, brand, model):
self.brand = brand
self.model = model
def get_description(self):
return f"{self.brand},{self.model}"
class Car(Vehicle):
def __init__(self, brand, model, num_doors):
super().__init__(brand, model)
self.num_doors = num_doors
def get_description(self):
base = super().get_description()
return f"base (Car, {self.num_doors} doors)"
class Motorcycle(Vehicle):
def __init__(self, brand, model, has_sidecar: bool):
super().__init__(brand,model)
self.has_sidecar = has_sidecar
def get_description(self):
base = super().get_description()
iscedou = " 有侧斗" if self.has_sidecar else " 没有侧斗"
return base + iscedou
# 测试
car = Car("Toyota", "Camry", 4)
moto = Motorcycle("Harley", "Sportster", False)
print(car.get_description())
print(moto.get_description())
运行结果:
python
base (Car, 4 doors)
Harley,Sportster 没有侧斗
题目3:多态 ------ 统一接口
在题目2类的基础上,写一个函数 describe_vehicles(vehicles),接收一个车辆列表,对每个车辆调用 get_description() 并打印。传入 Car 和 Motorcycle 的实例验证多态。
python
def describe_vehicles(vehicles):
for v in vehicles:
print(v.get_description())
fleet = [
Car("Honda", "Civic", 2),
Motorcycle("Yamaha", "MT-07", False),
Car("Ford", "Fusion", 4)
]
describe_vehicles(fleet)
运行结果:
python
base (Car, 2 doors)
Yamaha,MT-07 没有侧斗
base (Car, 4 doors)
题目4:魔术方法 ------ 自定义比较
创建一个 Student 类,有 name 和 score 属性。
-
重写
__eq__使得分数相等即认为学生相等 -
重写
__lt__使得按分数比较(低于) -
用
sorted()对一个学生列表排序,并打印结果 -
重写
__repr__为"Student(name, score)"方便调试
python
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
def __eq__(self, other):
if isinstance(other, Student):
return self.score == other.score
return NotImplemented
def __lt__(self, other):
if isinstance(other, Student):
return self.score < other.score
return NotImplemented
def __repr__(self):
return f"Student('{self.name}', {self.score})"
# 测试
students = [
Student("Tom", 85),
Student("Jerry", 92),
Student("Spike", 78)
]
print(sorted(students)) # [Student('Spike', 78), Student('Tom', 85), Student('Jerry', 92)]
print(Student("A", 85) == Student("B", 85))
运行结果:
python
[Student('Spike', 78), Student('Tom', 85), Student('Jerry', 92)]
True
题目5:简单继承与属性 ------ 文件系统模拟
设计一个 FileSystemNode 类,有 name 属性;一个 File 子类,额外有 size 属性;一个 Directory 子类,额外有 children 列表属性,并提供 add_child(node) 方法。
-
给每个类写一个
__str__方法,展示名称和(如果是文件)大小,如果是目录则展示子节点数量。 -
写一个测试,创建一个根目录,其中包含两个文件和另一个子目录,再打印结构。
python
class FileSystemNode:
def __init__(self, name):
self.name = name
class File(FileSystemNode):
def __init__(self, name, size):
super().__init__(name)
self.size = size
def __str__(self):
return f"{self.name},{self.size} bytes"
class Directory(FileSystemNode):
def __init__(self, name):
super().__init__(name)
self.children = []
def __str__(self):
return f"{self.name},节点数量为{len(self.children)}"
def add_child(self, node):
self.children.append(node)
# 测试
root = Directory("root")
root.add_child(File("file1", 1024))
root.add_child(File("file2", 4096))
root.add_child(Directory("child_directory"))
print(root)
for child in root.children:
print(child)
运行结果:
python
root,节点数量为3
file1,1024 bytes
file2,4096 bytes
child_directory,节点数量为0
四、文件读写
1)打开文件 ------ open()
python
f = open("test.txt", "r", encoding="utf-8")
data = f.read()
f.close() # 必须关闭!
常用模式
| 模式 | 含义 |
|---|---|
"r" |
只读(文件必须存在) |
"w" |
只写,清空已有内容(文件不存在则创建) |
"a" |
追加写 |
"r+" |
读写 |
"b" |
二进制模式,如 "rb", "wb"(读写图片、视频等) |
2)更优雅的方式 ------ with 语句(推荐!)
python
with open("test.txt", "r", encoding="utf-8") as f:
content = f.read()
# 这里 f 已经自动关闭
3)读取方法
python
# 1. 一次性读取整个文件(小心大文件)
text = f.read()
# 2. 读取一行
line = f.readline()
# 3. 读取所有行,返回列表
lines = f.readlines()
# 4. 最常用:直接迭代文件对象(惰性读取,内存友好)
for line in f:
print(line.strip())
4)写入方法
python
with open("output.txt", "w", encoding="utf-8") as f:
f.write("Hello\n") # 不会自动加换行
f.writelines(["a\n", "b\n"]) # 写入多个字符串
5)二进制读写 ------ 处理图片、音频等
python
with open("source.jpg", "rb") as src:
data = src.read() # 返回 bytes 对象,不是 str
with open("dest.jpg", "wb") as dst:
dst.write(data)
6)路径与文件系统
推荐用 pathlib 模块操作路径(Python 3.6+),比手动拼字符串更干净。
python
from pathlib import Path
path = Path("data") / "test.txt" # 跨平台拼接
text = path.read_text(encoding="utf-8")
7)文件读写练习题
题目1:实现简易 wc ------ 统计文件行数、单词数、字符数
新建一个文本文件 sample.txt,内容随意(英文)。编写程序读取该文件,输出行数、单词数(以空白字符分割)、字符数(含空格和换行)。
python
def wc(filename):
hs = 0
dcs = 0
zfs = 0
with open(filename, "r", encoding= "utf-8") as f:
for line in f:
hs += 1
dcs += len(line.split(" "))
zfs += len(line)
return hs, dcs, zfs
hs, dcs, zfs = wc("sample.txt")
print(f"行数:{hs},单词数:{dcs},字符数:{zfs}")
文本内容:
My favorite day at I can draw colorful
运行结果:
python
行数:2,单词数:8,字符数:38
题目2:读取 CSV 格式文件并处理
假设有一个 scores.csv 文件,内容如下(无标题行):
Alice,85 Bob,92 Charlie,78
请读取该文件,计算并打印平均分(保留一位小数),同时用列表推导式生成一个新列表,包含分数大于 80 的学生姓名。
python
sum = 0
rs = 0
lst1 = []
with open("scores.csv", "r", encoding="utf-8") as f:
for line in f:
rs += 1
sum += int(line.split(",")[1])
line = line.rstrip('\n')
lst1.append((line.split(',')[0], line.split(',')[1]))
avg = sum / rs
lst2 = [x[0] for x in lst1 if int(x[1]) > 80]
print(f"avg = {avg:.1f}")
print(lst1)
print(f"分数大于80的学生有:{lst2}")
运行结果:
python
avg = 85.0
[('Alice', '85'), ('Bob', '92'), ('Charlie', '78')]
分数大于80的学生有:['Alice', 'Bob']
题目3:写文件 ------ 生成乘法表
编写程序,将 9×9 乘法表输出到文件 multiplication.txt,格式如下:
1×1=1 1×2=2 2×2=4 ...
要求用 with 和 write 完成,每行末尾有换行。
python
with open("multiplication.txt", "w", encoding="utf-8") as f:
for i in range(1, 10):
for j in range(1, i+1):
f.write(f"{j}*{i}={j*i:2} ")
f.write('\n')
题目4:文件复制 ------ 二进制大文件分块读写
实现一个函数 copy_file(source, dest, buffer_size=8192),将源文件复制到目标文件,支持任意类型(文本、图片、视频),避免一次性读取大文件耗尽内存。
python
def copy_file(source, dest, buffer_size=8192):
with open(source, "rb") as rf, open(dest, "wb") as wf:
while True:
b = rf.read()
if not b:
break
wf.write(b)
copy_file("QQ20260624-175510.png", "QQ20260624-175511.png")
题目5:配置文件解析 ------ 异常处理与字典
给定配置文件 config.txt,每行格式为 key=value(忽略空行和以 # 开头的注释行)。请编写函数 parse_config(filename) 返回一个字典,键值都是字符串。
示例 config.txt:
python
# Database settings
host=localhost
port=5432
user=admin
要求:
-
使用
with和逐行迭代 -
捕获文件不存在的异常,打印错误并返回空字典
python
def parse_config(filename):
dct = {}
try:
with open(filename, "r", encoding="utf-8") as f:
for line in f:
if line[0] != '#':
line = line.rstrip('\n')
dct[line.split('=')[0]] = line.split('=')[1]
except FileNotFoundError:
print("文件不存在")
return dct
print(parse_config("config.txt"))
运行结果:
python
{'host': 'localhost', 'port': '5432', 'user': 'admin'}
五、异常处理
1)基本结构 ------ try/except/else/finally
python
try:
result = 10 / 0
except ZeroDivisionError:
print("不能除以零")
else:
print("没有异常时执行")
finally:
print("不管有没有异常,最后都执行")
-
except捕获指定异常类型,不写类型会捕获所有(不推荐)。 -
else子句:只在try块没有发生异常时执行,常用于"成功后的操作"。 -
finally子句:无论如何都执行 ,适合释放资源(但文件用with更好)。
2)常见异常类型(内置)
| 异常类 | 触发场景 | 类似 C 中的情况 |
|---|---|---|
ValueError |
类型对,值不对(如 int("abc")) |
sscanf 失败 |
TypeError |
操作类型不支持(如 "a" + 1) |
类型不匹配 |
IndexError |
列表索引越界 | 数组越界 |
KeyError |
字典键不存在 | 访问不存在的变量 |
FileNotFoundError |
文件不存在 | fopen 返回 NULL |
ZeroDivisionError |
除以零 | SIGFPE |
AttributeError |
对象没有该属性/方法 | 访问不存在的成员 |
NameError |
变量未定义 | 未声明 |
捕获多个异常:
python
try:
x = int(input("Enter: "))
except (ValueError, TypeError):
print("输入错误")
获取异常对象:
python
try:
x = 1 / 0
except ZeroDivisionError as e:
print(f"错误: {e}") # 错误: division by zero
3)主动抛出异常 ------ raise
python
def set_age(age):
if age < 0:
raise ValueError("年龄不能为负")
4)自定义异常
python
class MyError(Exception):
pass
raise MyError("something happened")
5)断言 ------ assert
python
x = -5
assert x >= 0, "x 不能为负" # 失败会抛 AssertionError
6)异常处理练习题
题目1:输入整数验证
编写一个函数 get_positive_int(prompt):
-
反复提示用户输入,直到用户输入一个有效的正整数。
-
使用异常处理应对非法输入(如字母、负数、小数、0)。
-
返回该正整数。
python
def get_positive_int(prompt):
while True:
try:
num = int(input(prompt))
if num <= 0:
raise ValueError
except ValueError:
print("您输入的不是整数,请重新输入")
else:
return num
print(f"您输入的正整数为:{get_positive_int('请输入一个正整数:')}")
运行结果:
python
请输入一个正整数:a
您输入的不是整数,请重新输入
请输入一个正整数:-1
您输入的不是整数,请重新输入
请输入一个正整数:0
您输入的不是整数,请重新输入
请输入一个正整数:1.1
您输入的不是整数,请重新输入
请输入一个正整数:3
您输入的正整数为:3
题目2:安全的字典取值与计算
写一个函数 safe_divide_from_dict(data, key_a, key_b):
-
接收字典
data和两个键。 -
尝试取出两个键对应的值,将其转为
float后计算除法a / b。 -
如果键不存在,捕获异常并提示哪个键缺失。
-
如果值不能转为数字,提示格式错误。
-
如果除数为零,提示不能除以零。
-
如果成功,返回计算结果(保留两位小数);否则返回
None。
python
def safe_divide_from_dict(data, key_a, key_b):
try:
value_a = float(data[key_a])
value_b = float(data[key_b])
result = value_a /value_b
return round(result, 2)
except KeyError as e:
print(f"键{e}不存在")
return None
except ValueError as e:
print(f"输入类型不正确,{e}")
return None
except ZeroDivisionError as e:
print(f"除数不能为0,{e}")
return None
# 测试
d = {"x": "10", "y": "0", "z": "hello"}
print(safe_divide_from_dict(d, "x", "y")) # 除数不能为零 -> None
print(safe_divide_from_dict(d, "x", "w")) # 键 'w' 不存在 -> None
print(safe_divide_from_dict(d, "x", "z")) # 值无法转换 -> None
print(safe_divide_from_dict(d, "x", "x")) # 10 / 10 = 1.0
运行结果:
python
除数不能为0,float division by zero
None
键'w'不存在
None
输入类型不正确,could not convert string to float: 'hello'
None
1.0
题目3:文件处理中的 finally/else
编写一个函数 count_lines(filename),统计指定文本文件的行数,并返回行数。要求
-
用
try/except/else/finally完整结构:-
try中打开文件并计数。 -
捕获
FileNotFoundError,打印 "文件不存在" 并返回 -1。 -
else中打印 "文件读取成功"。 -
finally中打印 "操作结束"。
-
python
def count_lines(filename):
try:
hs = 0
with open(filename, "r", encoding="utf-8") as f:
for line in f:
hs += 1
except FileNotFoundError:
print(f"文件{filename}不存在")
return -1;
else:
print("文件读取成功")
return hs
finally:
print("操作结束")
hs = count_lines("sample.txt")
if hs != -1:
print(f"文件sample.txt有{hs}行")
hs = count_lines("sample2.txt")
if hs != -1:
print(f"文件sample2.txt有{hs}行")
运行结果:
python
文件读取成功
操作结束
文件sample.txt有2行
文件sample2.txt不存在
操作结束
题目4:自定义异常与 raise
定义一个异常类 NegativeAgeError,继承自 ValueError。编写一个函数 create_person(name, age):
-
如果
age为负数,抛出NegativeAgeError(附带提示信息)。 -
如果
name为空字符串,抛出ValueError。 -
正常时返回一个包含姓名和年龄的字典。
编写测试代码,分别传入正确和错误参数,捕获并打印异常信息。
python
class NegativeAgeError(ValueError):
"""年龄为负数的异常"""
pass
def create_person(name, age):
try:
if age < 0:
raise NegativeAgeError
if name == "":
raise ValueError
return {'name':name, 'age':age}
except NegativeAgeError:
print("年龄错误,年龄为负数")
return None
except ValueError:
print("姓名错误,姓名为空")
return None
#测试
test_cases = [("Alice", 30), ("", 25), ("Bob", -5)]
for name, age in test_cases:
person = create_person(name, age)
if person != None:
print(f"创建成功: {person}")
运行结果:
python
创建成功: {'name': 'Alice', 'age': 30}
姓名错误,姓名为空
年龄错误,年龄为负数
题目5:异常链与转换
在 题目2 的基础上,改进 safe_divide_from_dict:如果除法时发生 ZeroDivisionError,将其转换为一个自定义的 CalculationError 异常(继承自 Exception),并保留原始异常信息。
使用 raise ... from ... 链式抛出。
python
class CalculationError(Exception):
pass
def safe_divide_from_dict_v2(data, key_a, key_b):
try:
value_a = float(data[key_a])
value_b = float(data[key_b])
result = value_a /value_b
return round(result, 2)
except KeyError as e:
print(f"键{e}不存在")
return None
except ValueError as e:
print(f"输入类型不正确,{e}")
return None
except ZeroDivisionError as e:
raise CalculationError("除数零导致计算错误") from e
# 测试
try:
d = {"x": "10", "y": "0"}
print(safe_divide_from_dict_v2(d, "x", "y"))
except CalculationError as e:
print(f"捕获自定义异常: {e}")
print(f"原始异常: {e.__cause__}")
# 输出:
# 捕获自定义异常: 除以零导致计算错误
# 原始异常:float division by zero
运行结果:
python
捕获自定义异常: 除数零导致计算错误
原始异常: float division by zero