九. 文件与数据持久化
程序运行起来后,数据不能只停留在内存里——下次重启时仍需保留。这就需要借助文件与数据持久化技术来兜底。从最简单的文本读写,到结构化的 CSV、JSON 格式,掌握这一系列操作后,日常开发中九成以上的场景都能轻松应对。
9.1 读写文本文件

在 Python 中操作文本文件,最推荐的写法就是 with open()——它能自动帮你释放资源,既省心又安全。模式主要有三种:写入(w)、追加(a)和读取(r)。写入模式会从头覆盖原有内容,追加模式则在末尾续写。注意编码统一使用 utf-8,否则中文很可能出现乱码。
# 写
with open("test.txt", "w", encoding="utf-8") as f:
f.write("第一行")
f.write("第二行")
# 追加
with open("test.txt", "a", encoding="utf-8") as f:
f.write("追加的行")
# 读所有
with open("test.txt", "r", encoding="utf-8") as f:
content = f.read()
print(content)
# 按行读(推荐)
with open("test.txt", "r", encoding="utf-8") as f:
for line in f:
print(line.strip())
按行读取的方式在处理大文件时特别实用,不会一次性将整个文件加载到内存中,能有效避免内存溢出。
9.2 CSV 文件操作
CSV 是数据交换中最常见的一种格式,Excel 可以直接打开,数据库也能轻松导入。Python 自带的 csv 模块完全够用,无需额外折腾。
import csv
# 写入
data = [["姓名", "年龄"], ["张三", 25], ["李四", 30]]
with open("people.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerows(data)
# 读取
with open("people.csv", "r", encoding="utf-8") as f:
reader = csv.reader(f)
for row in reader:
print(row)
写入 CSV 时务必加上 newline="",否则在 Windows 系统下默认产生的空行会让你很头疼。
9.3 JSON 数据交换格式
JSON 是 Web 开发中的通用语言,前后端通信、配置文件、API 响应等场景随处可见。json 模块的使用成本几乎为零——字典与 JSON 字符串的转换只需要一行代码。
import json
# 字典转 JSON 字符串
person = {"name": "王五", "age": 28, "hobby": ["读书", "跑步"]}
json_str = json.dumps(person, ensure_ascii=False, indent=2)
print(json_str)
# 保存到文件
with open("person.json", "w", encoding="utf-8") as f:
json.dump(person, f, ensure_ascii=False, indent=2)
# 读取 JSON
with open("person.json", "r", encoding="utf-8") as f:
loaded = json.load(f)
print(loaded["name"])
参数 ensure_ascii=False 是必坑的关键——如果不设置,中文会被转义成一堆 \u 编码。而 indent 纯粹为了让文件更易阅读,生产环境中可以省略。
十. 异常处理
代码不出错是不可能的。异常处理不是为了隐藏问题,而是为了让程序在遇到意外时能优雅地降级,或者至少给用户一个清晰的提示信息。
10.1 基本 try-except
try:
num = int(input("请输入整数: "))
result = 100 / num
print(result)
except ValueError:
print("输入的不是有效整数")
except ZeroDivisionError:
print("不能除以零")
except Exception as e:
print(f"其他错误: {e}")
else:
print("没有发生异常") # try 成功时执行
finally:
print("无论如何都会执行") # 清理资源等
几个细节:else 块仅在 try 成功时执行,finally 则无论是否发生异常都会执行——适合放置关闭文件、释放锁等清理操作。另外,捕获异常时应遵循“从具体到通用”的顺序,把 Exception 放在最后兜底。
10.2 抛出异常与自定义异常
def withdraw(balance, amount):
if amount > balance:
raise ValueError("余额不足")
return balance - amount
# 自定义异常类
class NegativeAmountError(Exception):
pass
def deposit(amount):
if amount < 0:
raise NegativeAmountError("存款金额不能为负")
自定义异常的好处是调用方可以精确捕获你定义的错误类型,而不是面对一个模糊的 ValueError。继承 Exception 即可,不要继承 BaseException——那是留给系统级异常的。
10.3 assert 断言(调试用)
def divide(a, b):
assert b != 0, "除数不能为零"
return a / b
# 运行时可用 -O 参数禁用断言
断言是调试利器,但不要用它来做真正的输入校验——生产环境可以通过 -O 参数直接禁用断言,到那时你的“保障”就完全失效了。
十一. 面向对象编程
面向对象不是仅仅学几个语法糖就能掌握的,关键是理解封装、继承和多态。这里把最常用的特性串一遍,足够你在实际项目中写出清晰且易于维护的类。
11.1 类与对象回顾
class Student:
school = "第一中学" # 类属性,所有实例共享
def __init__(self, name, score):
self.name = name # 实例属性
self.score = score
def introduce(self):
print(f"{self.name} 来自 {self.school}")
s1 = Student("小明", 90)
s1.introduce()
类属性是“模板级别的”,所有实例共用;实例属性才是每个对象自己的。两者千万不要混用,否则一旦修改就会影响所有实例。
11.2 属性装饰器(@property)
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if value < 0:
raise ValueError("半径不能为负")
self._radius = value
@property
def area(self):
return 3.14 * self._radius ** 2
c = Circle(5)
print(c.area) # 像属性一样访问,无需括号
c.radius = 10 # 使用 setter
@property 让方法伪装成属性,既保留了数据校验的空间,调用者也无需加括号。setter 中可以加入业务规则——比如这里半径不能为负。
11.3 类方法与静态方法
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
@classmethod
def from_string(cls, date_str):
# 类方法用于创建实例的替代构造函数
year, month, day = map(int, date_str.split("-"))
return cls(year, month, day)
@staticmethod
def is_valid(year, month, day):
# 静态方法,不依赖实例或类
return 1 <= month <= 12 and 1 <= day <= 31
d = Date.from_string("2025-12-25")
print(Date.is_valid(2025, 13, 1)) # False
类方法最常见的用途是提供多个构造函数,比如这里的 from_string。静态方法更像是一个“工具函数”,跟类沾边但不依赖实例,适合用作辅助校验。
11.4 继承与 super()
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
raise NotImplementedError
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name) # 调用父类初始化
self.breed = breed
def speak(self):
return f"{self.name} 汪汪叫"
class Cat(Animal):
def speak(self):
return f"{self.name} 喵喵喵"
animals = [Dog("旺财", "金毛"), Cat("咪咪")]
for a in animals:
print(a.speak())
子类中使用 super() 调用父类的 __init__ 是标准写法,这样父类的初始化逻辑不会丢失。多态体现在 speak() 方法上——每种动物各有各的叫声,循环中完全不需要关心具体类型。
11.5 魔术方法(双下划线方法)
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Vector({self.x}, {self.y})"
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __len__(self):
return 2
v1 = Vector(1,2)
v2 = Vector(3,4)
print(v1 + v2) # Vector(4,6)
print(len(v1)) # 2
魔术方法让你的自定义类支持 Python 原生的操作符,比如 +、len()、print() 等。善用它们,代码会更符合“Python 风格”,而不是 Java 的翻译版。
