当前位置: 首页 > news >正文

Python编程基础(九) | 文件和异常


引言:很久没有写 Python 了,有一点生疏。这是学习《Python 编程:从入门到实践(第3版)》的课后练习记录,主要目的是快速回顾基础知识。

练习1: Python学习笔记

在文本编辑器中新建一个文件 learning_python.txt,写几句话总结你学到的Python知识,每行都以“In Python you can”开头。编写一个程序,读取这个文件,并将内容打印两次:第一次读取整个文件,第二次遍历文件对象。

from pathlib import Path# 假设 learning_python.txt 文件内容如下:
# In Python you can store data in variables.
# In Python you can use loops to iterate over items.
# In Python you can write functions to organize code.path = Path('learning_python.txt')# 方法一:读取并打印整个文件
print("--- Reading the entire file at once: ---")
contents = path.read_text()
print(contents)# 方法二:遍历文件中的每一行
print("\n--- Looping over the file's lines: ---")
lines = contents.splitlines()
for line in lines:print(line)
--- Reading the entire file at once: ---
In Python you can store data in variables.
In Python you can use loops to iterate over items.
In Python you can write functions to organize code.--- Looping over the file's lines: ---
In Python you can store data in variables.
In Python you can use loops to iterate over items.
In Python you can write functions to organize code.

知识点回顾:

  • pathlib 模块: 这是处理文件路径的现代化、面向对象的方式。Path('filename.txt') 创建一个路径对象。
  • 读取文件: path.read_text() 方法一次性读取文件的全部内容,并返回一个字符串。这对于小文件非常方便。
  • 处理行: contents.splitlines() 方法将一个大字符串按行分割,并返回一个包含所有行的列表。
  • 逐行遍历: 使用 for 循环遍历由 splitlines() 生成的列表,是处理文件内容最常见的方式之一。

练习2: C语言学习笔记

读取你创建的 learning_python.txt 文件,将其中的 “Python” 都替换为另一门语言的名称(如 “C”),并将修改后的各行打印到屏幕上。

from pathlib import Pathpath = Path('learning_python.txt')
contents = path.read_text()for line in contents.splitlines():# 使用 replace() 方法进行替换modified_line = line.replace('Python', 'C')print(modified_line)
In C you can store data in variables.
In C you can use loops to iterate over items.
In C you can write functions to organize code.

知识点回顾:

  • 字符串方法 replace(): string.replace('old', 'new') 会返回一个新的字符串,其中所有出现的 'old' 子字符串都被 'new' 替换。原始字符串 line 本身保持不变。
  • 方法应用: 这个练习展示了如何将文件读取与字符串处理方法结合起来,对文件内容进行动态修改和输出。

练习3:简化代码

在前面的程序中,我们通常会用一个临时变量来存储 splitlines() 的结果。实际上,可以直接遍历 contents.splitlines() 返回的列表来让代码更简洁。

from pathlib import Path# 以练习二为例进行简化
path = Path('learning_python.txt')# 将 read_text() 和 splitlines() 链式调用
for line in path.read_text().splitlines():print(line.replace('Python', 'Rust'))
in Rust you can ...1
in Rust you can ...2
in Rust you can ...3

知识点回顾:

  • 方法链式调用 (Method Chaining):当一个方法返回一个对象时,可以立即在该对象上调用另一个方法,如 path.read_text().splitlines()。这使得代码更紧凑。
  • 代码简洁性:通过省略不必要的临时变量,代码的行数减少,意图也可能更直接。不过,在复杂操作中,使用临时变量有时能提高代码的可读性。

练习4:访客

编写一个程序,提示用户输入名字,然后将其名字写入文件 guest.txt

from pathlib import Pathname = input("请输入你的名字:")
path = Path("guest.txt")
path.write_text(name)print(f"你好, {name},你的名字已被记录。")
请输入你的名字:孔乙己
你好, 孔乙己,你的名字已被记录。```**知识点回顾:**
*   **用户输入 `input()`**: `input()` 函数用于从用户那里获取文本输入,它总是返回一个字符串。
*   **写入文件 `write_text()`**: `path.write_text(data)` 会将字符串 `data` 写入到文件中。**注意**:此方法会覆盖文件的所有现有内容。如果文件不存在,它会自动创建。### 练习5:访客簿
> 编写一个 `while` 循环,提示用户输入名字。收集所有输入的名字,并将它们写入 `guest_book.txt`,确保每条记录都独占一行。```python
from pathlib import Pathpath = Path("guest_book.txt")
guest_list = []while True:name = input("请输入你的名字 (输入 'q' 退出): ")if name == 'q':break# 将名字和换行符一起添加到列表中guest_list.append(name + '\n')# 将列表中的所有字符串连接起来并写入文件
path.write_text("".join(guest_list))
print("访客名单已保存。")
请输入你的名字 (输入 'q' 退出): 孔乙己
请输入你的名字 (输入 'q' 退出): 阿Q
请输入你的名字 (输入 'q' 退出): 祥林嫂
请输入你的名字 (输入 'q' 退出): q
访客名单已保存。

知识点回顾:

  • while 循环: 用于重复执行代码块,直到特定条件(用户输入’q’)满足为止。
  • 哨兵值 (Sentinel Value):‘q’ 在这里是一个哨兵值,它的出现标志着循环的结束。
  • 换行符 \n: 在字符串末尾添加 \n,可以确保在写入文件时内容会换行。
  • 构建内容后一次性写入: 先将所有内容收集到一个列表 guest_list 中,然后使用 "".join(guest_list) 将它们合并成一个大字符串,最后一次性写入文件。这通常比在循环中反复打开和写入文件更高效。

练习6:加法运算

编写一个程序,提示用户输入两个数,然后将它们相加。捕获 ValueError 异常,以防用户输入的不是数字。

try:num1 = float(input("请输入第一个数:"))num2 = float(input("请输入第二个数:"))
except ValueError:print("输入错误,请输入有效的数字!")
else:result = num1 + num2print(f"两数之和为:{result}")
请输入第一个数:10
请输入第二个数:a
输入错误,请输入有效的数字!

知识点回顾:

  • 异常处理 try-except: 这是 Python 中处理错误的强大机制。try 块中的代码是被监控的,如果发生特定类型的错误(如 ValueError),程序会跳转到相应的 except 块执行,而不是直接崩溃。
  • ValueError: 当一个函数(如 float())接收到一个类型正确但值不合适的参数时,会引发此异常。例如,float('a') 就会引发 ValueError
  • else 代码块: try 块中没有发生异常时,else 块中的代码会被执行。这非常适合放置只有在 try 块成功后才应运行的代码。

练习7:加法计算器

将练习6的代码放入一个 while 循环中,让用户可以持续进行计算,直到成功完成一次加法运算。

while True:try:a = input("请输入第一个数字 (或输入'q'退出): ")if a.lower() == 'q':breakb = input("请输入第二个数字 (或输入'q'退出): ")if b.lower() == 'q':breakresult = float(a) + float(b)except ValueError:print("输入错误,请输入有效的数字!")else:print(f'数之和为:{result}')print("计算成功,程序结束。")break
请输入第一个数字 (或输入'q'退出): 1
请输入第二个数字 (或输入'q'退出): q
请输入第一个数字 (或输入'q'退出): 1
请输入第二个数字 (或输入'q'退出): 3
数之和为:4.0
计算成功,程序结束。

知识点回顾:

  • 循环与异常处理的结合: 这是创建健壮的、交互式程序的常用模式。循环确保程序持续运行,而 try-except 块处理每次迭代中可能出现的错误输入,防止程序因单次错误而终止。
  • break 语句: 用于立即退出当前的 whilefor 循环。在这个例子中,它被用于成功计算后或用户主动选择退出时结束程序。

练习8:猫和狗

创建 cats.txtdogs.txt 两个文件。编写一个程序,尝试读取并打印这两个文件的内容,使用 try-except 块处理 FileNotFoundError 异常。

from pathlib import Pathdef read_animal_names(path: Path) -> None:"""尝试读取并打印一个文件,如果文件不存在则打印错误信息。"""try:contents = path.read_text()print(f"--- 内容来自 {path.name} ---")print(contents)except FileNotFoundError:print(f"错误:找不到文件 {path}。")# 假设 cats.txt 存在,而 dogs.txt 不存在
get_file(Path("cats.txt"))
get_file(Path("dogs.txt"))
--- 内容来自 cats.txt ---
Mimi
Kitty
Garfield错误:找不到文件 dogs.txt。

知识点回顾:

  • FileNotFoundError: 当尝试打开或操作一个不存在的文件时,Python 会引发这个特定的异常。
  • 优雅地处理错误: 通过捕获 FileNotFoundError,程序可以在某个文件缺失时继续执行,而不是崩溃。这对于处理可选的配置文件或数据文件非常有用。
  • 代码封装: 将文件读取逻辑封装在一个函数 (read_animal_names) 中,使得代码更易于重用和理解。

练习9:静默的猫和狗

修改练习8的 except 块,让程序在文件不存在时静默失败(即什么也不做)。

from pathlib import Pathdef read_animal_names_silently(path: Path) -> None:"""尝试读取文件,如果文件不存在则忽略。"""try:contents = path.read_text()print(f"--- 内容来自 {path.name} ---")print(contents)except FileNotFoundError:# 文件不存在时,什么也不做passread_animal_names_silently(Path("cats.txt"))
read_animal_names_silently(Path("dogs.txt"))
--- 内容来自 cats.txt ---
Mimi
Kitty
Garfield

知识点回顾:

  • pass 语句: pass 是一个空操作占位符。当语法上需要一个语句,但程序不需要执行任何操作时,就可以使用它。
  • 静默失败 (Silent Failure): 在 except 块中使用 pass 会导致错误被“吞噬”,程序会悄无声息地继续执行。这在某些情况下是期望的行为(例如,加载一个可选的配置文件),但需要谨慎使用,因为它也可能隐藏真正的问题。

练习10:常见单词

编写一个程序,读取一个文本文件,并计算单词 “the” 在其中出现了多少次。

from pathlib import Pathdef count_specific_word(path: Path, word: str) -> int:"""计算一个特定单词在文件中出现的次数(忽略大小写)。"""try:contents = path.read_text(encoding='utf-8')except FileNotFoundError:print(f'错误:文件 {path} 不存在。')return 0# 转换为小写以进行不区分大小写的计数word_count = contents.lower().count(f" {word} ")return word_countpath = Path('moby_dick.txt') # 假设从古登堡计划下载了《白鲸》
the_count = count_specific_word(path, 'the')# 直接使用 count() 会包含 'then', 'there' 等
inaccurate_count = path.read_text(encoding='utf-8').lower().count('the')print(f"使用 str.count('the') 的结果 (不准确): {inaccurate_count}")
print(f"计算独立的单词 'the' 的结果 (更准确): {the_count}")
使用 str.count('the') 的结果 (不准确): 14431
计算独立的单词 'the' 的结果 (更准确): 13721

知识点回顾:

  • str.lower(): 将整个字符串转换为小写,这是进行不区分大小写文本分析的第一步。
  • str.count(): 一个简单快捷的方法,用于计算子字符串在字符串中出现的次数。
  • 分析的精确性: 直接使用 count('the') 会错误地计算包含 “the” 的单词(如 “there”, “other”)。通过搜索 f" {word} "(单词两边带空格)是一种更精确的近似方法,尽管它会漏掉句首和句尾的 “the”。一个更完善的解决方案需要使用正则表达式或更复杂的文本分割。

练习11 & 12:记住喜欢的数

编写一个程序,如果用户喜欢的数字已经存储,则读取并显示它。否则,提示用户输入并将其存储在文件中。

from pathlib import Path
import jsonpath = Path("favorite_number.json")try:# 尝试读取文件contents = path.read_text()number = json.loads(contents)
except (FileNotFoundError, json.JSONDecodeError):# 如果文件不存在或内容不是有效的JSON,则获取新数字number = input("你最喜欢的数字是什么? ")contents = json.dumps(number)path.write_text(contents)print("谢谢,我已经记住你的数字了!")
else:# 如果读取成功,则显示它print(f"我知道你最喜欢的数字!它是 {number}。")
# 第一次运行
你最喜欢的数字是什么? 7
谢谢,我已经记住你的数字了!# 第二次运行
我知道你最喜欢的数字!它是 7。

知识点回顾:

  • json 模块: Python 的标准库,用于处理 JSON (JavaScript Object Notation) 数据格式。
  • json.dumps() (dump string): 将 Python 对象(如数字、字符串、列表、字典)序列化成 JSON 格式的字符串。
  • json.loads() (load string): 将 JSON 格式的字符串反序列化成 Python 对象。
  • 数据持久化: 使用 json 和文件 I/O,可以让程序“记住”上次运行时的数据,实现了简单的持久化存储。

练习13 & 14:验证用户

扩展 remember_me.py 示例,存储更多用户信息(姓名、性别、年龄),并在程序启动时验证当前用户是否是上一次的用户。

from pathlib import Path
import jsondef get_stored_user(path: Path) -> dict | None:"""如果存在,则加载存储的用户信息。"""if path.exists():try:user_data = json.loads(path.read_text())return user_dataexcept json.JSONDecodeError:return None # 文件损坏或为空return Nonedef get_new_user(path: Path) -> dict:"""获取新的用户信息并保存。"""user = {}user['name'] = input("你的名字是? ")user['gender'] = input("你的性别是? ")user['age'] = input("你的年龄是? ")path.write_text(json.dumps(user))return userdef greet_user():"""问候用户,并确认身份。"""path = Path('user.json')user = get_stored_user(path)if user:# 验证用户prompt = f"这是你吗, {user['name']}? (y/n) "if input(prompt).lower() == 'y':print(f"欢迎回来, {user['name']}!")print(f"- 性别: {user['gender']}, 年龄: {user['age']}")else:# 不是同一个用户,获取新用户信息user = get_new_user(path)print(f"好的,{user['name']},我们会记住你的信息。")else:# 没有存储的用户,获取新用户信息user = get_new_user(path)print(f"好的,{user['name']},我们会记住你的信息。")greet_user()
# 第一次运行
你的名字是? 张三
你的性别是? 男
你的年龄是? 18
好的,张三,我们会记住你的信息。# 第二次运行 (由张三本人)
这是你吗, 张三? (y/n) y
欢迎回来, 张三!
- 性别: 男, 年龄: 18# 第三次运行 (由李四)
这是你吗, 张三? (y/n) n
你的名字是? 李四
你的性别是? 男
你的年龄是? 20
好的,李四,我们会记住你的信息。

知识点回顾:

  • 存储复杂数据: JSON 非常适合存储像字典这样的结构化数据,可以轻松地保存多个相关信息。
  • 代码重构: 将逻辑划分为独立的函数 (get_stored_user, get_new_user, greet_user) 极大地提高了代码的可读性、可维护性和重用性。
  • 用户验证: 在加载持久化数据后增加一个验证步骤,是提高应用程序健壮性和用户体验的重要实践。它确保了程序不会错误地将前一个用户的数据应用到当前用户身上。

文章转载自:

http://xaqQ76Ap.rwLsr.cn
http://d7G8m1CM.rwLsr.cn
http://NwBkcd0A.rwLsr.cn
http://FiLFpdU6.rwLsr.cn
http://A8WBqVO9.rwLsr.cn
http://mbHRRcht.rwLsr.cn
http://ubKnt3iu.rwLsr.cn
http://1nxyphwh.rwLsr.cn
http://UhS4q8Bs.rwLsr.cn
http://eqHan6JN.rwLsr.cn
http://QSPj9qz3.rwLsr.cn
http://AZHcIeCX.rwLsr.cn
http://v3yLsCKx.rwLsr.cn
http://kuF3yJ5w.rwLsr.cn
http://pAgq21u7.rwLsr.cn
http://bScB0fGl.rwLsr.cn
http://GI2AU7Es.rwLsr.cn
http://gncHUFzK.rwLsr.cn
http://7izglVNe.rwLsr.cn
http://6HTOyPsA.rwLsr.cn
http://Cph9LjLK.rwLsr.cn
http://0t3yk1Kr.rwLsr.cn
http://85qLFFxW.rwLsr.cn
http://Y0ifV6fK.rwLsr.cn
http://s4lIaILv.rwLsr.cn
http://3BeYPtoB.rwLsr.cn
http://OMHagxvQ.rwLsr.cn
http://dbdTxgW9.rwLsr.cn
http://hOM0M5KF.rwLsr.cn
http://Yewe7q0r.rwLsr.cn
http://www.dtcms.com/a/379566.html

相关文章:

  • AWS IAM条件操作符实战指南:从基础到高级应用
  • SW - 无法用此剖切线来剖切此模型/零部件。请确认该剖切线完全通过该模型。
  • 【主页介绍】
  • 数据治理进阶——解读2024 企业数据治理体系和应用场景案例【附全文阅读】
  • 测试的概念
  • Python生物信息学数据处理大全:从FASTA文件到Pandas DataFrame
  • Android 设置禁止截图和禁止长截图
  • VR煤矿实训系统相较于传统煤矿培训方式的独特优势​-广州华锐互动
  • 鸿蒙Next Web组件详解:属性设置与事件处理实战
  • Chaosblade常用命令和范例
  • Linux内存管理章节九: 打通虚拟与实体的桥梁:深入Linux内存映射机制
  • leetcode13:罗马数字转整数(哈希表模拟)
  • TCP协议的相关特性
  • 猎豹移动2025年Q2财报:营收2.952亿,接近盈亏平衡
  • Spring框架1—Spring的IOC核心技术1
  • LeetCode 2327.知道秘密的人数:动态规划/差分数组O(n)
  • 8年老测试分析,自动化测试的挑战与实施,一篇打通...
  • VBA即用型代码手册:另存为html文件SaveAs .Html File
  • 数字孪生:数据驱动下的虚实融合与技术落地方法论
  • 【前端Vue】el-dialog关闭后黑色遮罩依然存在如何解决?
  • 计算机视觉与模式识别前沿一览:2025年8月arXiv 热点研究趋势解析
  • 【Java】P1 Java由此开始:简介、下载安装与HelloJava
  • Katalog:AI语音文章播报工具,打造沉浸式听读体验
  • 细胞图像分割实战:用U-Net模型自动识别显微镜图像中的细胞
  • 如何理解MOS管规格书中标注的VDS?
  • JavaScript逆向SM国密算法
  • 炫彩VS动作指令:活体检测技术大比拼
  • 只读查询的“零分配”之路:EF Core + Dapper + MemoryPack 的组合优化
  • EMC电磁兼容进阶3讲培训:专题三 近场探头和频谱仪在EMC整改中的应用
  • 清理C盘回忆录