今天学习文件 I / O 与序列化:
1. 文件读写
在 Python 里读写文件是非常常见的操作——读配置、写日志、处理数据文件等等,核心就一个函数open():
# 读文件f=open("test.txt","r",encoding="utf-8")content=f.read()f.close()# 写文件f=open("output.txt","w",encoding="utf-8")f.write("Hello\n")f.close()open(file, mode, encoding)函数三个常用参数,
file:必填,文件路径(相对路径或绝对路径)。
mode:可选,文件打开模式,默认值为'r'(只读文本模式)。
| 模式 | 含义 | 文件不存在时 |
|---|---|---|
| “r” | 只读(默认) | 报错 |
| “w” | 写入(覆盖) | 创建新文件 |
| “a” | 追加 | 创建新文件 |
| “r+” | 读写 | 报错 |
encoding:可选,文件编码格式,文本模式下生效。强烈建议显式指定,Windows 系统默认 GBK,Linux/macOS 默认 UTF-8,显式指定可避免乱码,常用encoding='utf-8'。
但这种方式有个问题——如果中间报错,f.close()不会执行,文件句柄就泄漏了。所以 Python 推荐用 with 语句:
# with 自动帮你 close,即使报错也会关闭withopen("test.txt","r",encoding="utf-8")asf:content=f.read()# 出了 with 块,f 自动关闭注:as可以给打开的文件起别名f。
读取文件技巧:
# 方法1:readlines() 一次全部读入(小文件)withopen("big.txt","r",encoding="utf-8")asf:lines=f.readlines()# 返回列表,每行一个元素# 方法2:逐行迭代(大文件推荐,不占内存)withopen("big.txt","r",encoding="utf-8")asf:forlineinf:# 文件对象本身是迭代器print(line.strip())# strip() 去掉末尾换行符2. JSON
JSON 作为一种轻量级、跨语言通用的文本数据交换格式,在Python中具有内置的json标准库可以直接完成两者互转,而在json文件文件读写过程中进行格式转换的函数有:
| 函数 | 参数 | 作用 |
|---|---|---|
json.loads(string) | 字符串 | JSON 字符串 → Python 对象 |
json.load(file) | 文件对象 | 从 文件 读取 JSON → Python 对象 |
json.dumps(obj) | python对象 | Python 对象 → JSON 字符串 |
json.dump(obj, file) | python对象,文件对象 | Python 对象 → 写入 文件 |
importjson# loads:字符串 → Pythondata=json.loads('{"name": "Alice", "age": 25}')print(data["name"])# Alice# load:文件 → Pythonwithopen("data.json","r")asf:data=json.load(f)3. pathlib: 现代路径处理
以前处理文件路径用 os.path,但 Python 3.4+ 推出了 pathlib,更优雅:
frompathlibimportPath# 创建路径对象p=Path("data")/"users"/"info.json"# 用 / 拼接路径,跨平台自动处理# 常用操作p.exists()# 文件是否存在?p.is_file()# 是文件吗?p.is_dir()# 是目录吗?p.read_text()# 读取文本内容(一步到位)p.write_text("hello")# 写入文本p.suffix# 获取扩展名,如 ".json"p.stem# 不含扩展名的文件名p.parent# 父目录# 遍历目录forfinPath(".").glob("*.py"):# 找当前目录所有 .py 文件print(f.name)pathlib vs os.path:pathlib 用 / 拼接路径更直观,而且返回的是 Path 对象可以直接调用方法,不用像 os.path 那样传来传去。
使用 pathlib 读写:
frompathlibimportPath# 读content=Path("config.json").read_text(encoding="utf-8")data=json.loads(content)# 写Path("output.txt").write_text("Hello World",encoding="utf-8")# JSON 也可以结合data={"key":"value"}Path("out.json").write_text(json.dumps(data,ensure_ascii=False,indent=2),encoding="utf-8")4. pickle: Python 专属序列化
JSON 是通用格式(任何语言都能读),但有个限制——只支持基本类型(字符串、数字、列表、字典)。如果你有一个 Python 对象(比如类的实例、集合、日期对象),JSON 就搞不定了。
这时候用 pickle:
importpickle# 序列化:Python 对象 → 字节流data={"name":"Alice","scores":{95,87,92}}# 注意有 set,JSON 不支持withopen("data.pkl","wb")asf:# 注意是 "wb" 二进制写模式pickle.dump(data,f)# 反序列化:字节流 → Python 对象withopen("data.pkl","rb")asf:loaded=pickle.load(f)print(loaded)# {'name': 'Alice', 'scores': {87, 92, 95}}对应的文件后缀也要由 .json 改成 .pkl , 文件打开模式要改成 “wb”、“rb”,前者是二进制写入模式,后者是二进制只读模式,也不需要指定编码格式了。
pickle vs JSON 对比:
| 特性 | JSON | pickle |
|---|---|---|
| 跨语言 | 通用(JS/Go/Java 都能读) | Python 专属 |
| 支持类型 | 基本类型(str/int/list/dict) | 几乎所有 Python 对象 |
| 安全性 | 安全 | 不安全——加载恶意 pickle 可执行任意代码 |
| 可读性 | 人能直接读 | 二进制,人读不了 |
| 用途 | API 传输、配置文件 | 缓存、临时存储 Python 对象 |
安全警告:永远不要 pickle.load() 来路不明的文件,它能执行任意代码。
5. YAML:配置文件格式
YAML 是另一种数据格式,常见于配置文件(比如 Docker Compose、GitHub Actions、K8s):
# config.ymldatabase:host:localhostport:5432name:myapplogging:level:infofile:/var/log/app.log读取 YAML 需要第三方库:
importyamlwithopen("config.yml","r",encoding="utf-8")asf:config=yaml.safe_load(f)# 注意用 safe_load,不是 loadprint(config["database"]["host"])# localhost注:YAML 不是 Python 内置的,需要pip install pyyaml。记住用 safe_load 而非 load,理由和 pickle一样——安全。
6. 四种格式总结
| 格式 | 用途 | Python 库 | 特点 |
|---|---|---|---|
| JSON | API 数据交换、轻量存储 | json(内置) | 通用、可读、只支持基本类型 |
| pickle | 缓存 Python 对象 | pickle(内置) | 支持任意对象、不安全、二进制 |
| YAML | 配置文件 | pyyaml(第三方) | 可读性最好、缩进敏感 |
| 纯文本 | 日志、数据文件 | open() / pathlib | 最基础 |