python快速学习&练习题巩固

一、流程控制

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 类:

  • 初始化传入 widthheight

  • 提供 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() 并打印。传入 CarMotorcycle 的实例验证多态。

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 类,有 namescore 属性。

  • 重写 __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
...

要求用 withwrite 完成,每行末尾有换行。

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