Python 序列化格式速查:JSON / Pickle / CSV / Parquet / Protobuf 怎么选

2026-06-11 33 预计阅读时间: 1 分钟
来源: realpython.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:7 分钟

把 Python 对象写到文件或传给另一个服务,选错序列化格式,后果比写错代码还烦——磁盘占用翻倍、跨语言对接卡壳、甚至 pickle 反序列化直接被注入恶意代码。五种主流格式各有明确适用场景,这篇把它们的边界和用法一次讲清。

五种格式的核心差异

先看一张对比表,心里有个锚:

格式 人类可读 跨语言 典型体积 适合场景
JSON API 交互、配置文件、前端对接
CSV 小(纯表格) 表格数据导出、Excel/BI 导入
Pickle ❌(仅 Python) Python 内部缓存、短期持久化
Parquet ✅(生态广) 小(列存压缩) 大规模分析、Spark / Pandas 批处理
Protobuf ✅(多语言 SDK) 高频 RPC、微服务间通信

体积和可读性是鱼和熊掌,但更关键的问题是:数据消费者是谁? 如果下游是浏览器,Parquet 再高效也白搭;如果另一端是 Go 微服务,Pickle 直接出局。

JSON:最稳的通用货币

JSON 不需要额外依赖,json 模块是标准库的一部分。它的限制同样明确——只支持基本类型,自定义对象需要你自己转换。

import json
from datetime import datetime

# 自定义对象 → JSON 的典型做法
class User:
    def __init__(self, name, created_at):
        self.name = name
        self.created_at = created_at

    def to_dict(self):
        # datetime 不是 JSON 原生类型,必须转字符串
        return {
            "name": self.name,
            "created_at": self.created_at.isoformat(),
        }

    @classmethod
    def from_dict(cls, d):
        return cls(d["name"], datetime.fromisoformat(d["created_at"]))

user = User("alice", datetime.now())

# 序列化
payload = json.dumps(user.to_dict(), ensure_ascii=False, indent=2)
print(payload)

# 反序列化
restored = User.from_dict(json.loads(payload))
print(restored.name, restored.created_at)

运行前不需要安装任何东西。ensure_ascii=False 让中文直接输出而不是 \uXXXX 转义;indent=2 方便调试,生产环境可以去掉以减小体积。

Pickle:快但危险的内部通道

Pickle 能序列化几乎所有 Python 对象,包括函数、类实例,代价是两点:只 Python 能读反序列化不可信数据等于远程代码执行

import pickle

# Pickle 直接序列化自定义对象,无需 to_dict
user = User("bob", datetime.now())

with open("user.pkl", "wb") as f:
    pickle.dump(user, f)

with open("user.pkl", "rb") as f:
    restored = pickle.load(f)  # 直接拿到 User 实例

print(restored.name, restored.created_at)

关键原则:永远不要 pickle.load 来自网络或不可信来源的数据。只在自己进程间或可信存储里用它,比如把训练好的 sklearn 模型存到本地磁盘。

CSV:表格数据的最低摩擦路径

CSV 的优势不是效率,而是接收方零门槛——Excel、pandas、数据库都能直接读。

import csv

users = [
    {"name": "alice", "score": 92},
    {"name": "bob", "score": 85},
    {"name": "carol", "score": 78},
]

# 写入
with open("scores.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=["name", "score"])
    writer.writeheader()
    writer.writerows(users)

# 读取
with open("scores.csv", "r", encoding="utf-8") as f:
    for row in csv.DictReader(f):
        print(row["name"], row["score"])

CSV 只适合扁平表格。嵌套结构需要先 flatten 或改用 JSON。

Parquet:列存压缩,分析场景的性价比之王

Parquet 把数据按列存储并自动压缩,同等内容比 JSON 小 5–10 倍是常态。pandas 直接支持读写。

# 先装依赖
pip install pandas pyarrow
import pandas as pd

df = pd.DataFrame(users)  # 上面的 users 列表

# 写入 Parquet,自动压缩
df.to_parquet("scores.parquet", engine="pyarrow", compression="snappy")

# 读取——只加载需要的列,省内存
df_scores = pd.read_parquet("scores.parquet", columns=["score"])
print(df_scores.describe())

当数据量从几千行涨到百万行,Parquet 的列裁剪和压缩优势会非常明显。如果下游是 Spark 或 DuckDB,Parquet 是默认选择。

Protocol Buffers:微服务通信的纪律性方案

Protobuf 强制你先写 schema(.proto 文件),再生成多语言代码。前期多一步,换来的是严格的类型约束和极小的序列化体积。

pip install protobuf

先写 schema 文件 user.proto

syntax = "proto3";

message User {
  string name = 1;
  int32 score = 2;
}

用 protoc 生成 Python 代码:

protoc --python_out=. user.proto

然后在 Python 里使用:

# 生成的模块名是 user_pb2
import user_pb2

user = user_pb2.User()
user.name = "alice"
user.score = 92

# 序列化成二进制,体积远小于 JSON
data = user.SerializeToString()
print(f"字节长度: {len(data)}")

# 反序列化
restored = user_pb2.User()
restored.ParseFromString(data)
print(restored.name, restored.score)

Protobuf 的纪律性在多人协作时是优势:schema 就是契约,字段增删有明确规则(proto3 新字段不影响旧代码解析),不会出现 JSON 里"这个字段到底有没有"的模糊地带。

选型决策清单

遇到序列化需求时,按这个顺序判断:

  1. 下游是不是 Python?且数据来源可信? → Pickle 最省事,但别跨进程边界用。
  2. 需要人能直接看/编辑? → JSON(嵌套结构)或 CSV(纯表格)。
  3. 数据量超过百万行,下游做分析? → Parquet,配合 pandas / Spark。
  4. 多语言微服务高频通信? → Protobuf,用 schema 约束接口。
  5. 以上都不满足,只是临时存一下? → JSON,别过度设计。

一个常见陷阱是"哪个格式最先进就用哪个"。Parquet 和 Protobuf 确实高效,但如果你的数据只有 200 行、下游是 Excel 业务人员,CSV 才是正确答案。格式选择不是性能竞赛,而是匹配数据的生产者和消费者


相关推荐