跳到主要内容
版本:dev

Python 原生序列化

Python 原生序列化是通过 xlang=False 选择的仅限 Python 编码模式。当所有写入端和读取端都是 Python,并且载荷应遵循 Python 对象模型而非可移植的跨语言类型系统时,请使用该模式。

如果字节必须由 Java、C++、Go、Rust、JavaScript/TypeScript、C#、Swift、Dart、Scala、Kotlin 或其他非 Python Fory 实现读取,请使用 Python 默认的跨语言序列化模式。

何时使用原生序列化

以下情况使用原生序列化:

  • 载荷仅由 Python 应用生成和使用。
  • 正在为仅限 Python 的对象图替代 picklecloudpickle
  • 数据模型包含函数、lambda、本地类、方法或 Python 归约钩子。
  • 对象图可能包含需要 Python 引用跟踪的共享对象或循环。
  • 大型 Python 数据对象需要 pickle 协议 5 风格的带外缓冲区。

原生模式可以序列化全局函数、本地函数、lambda、本地类、方法,以及使用 __getstate____setstate____reduce____reduce_ex__ 自定义的对象等 Python 专用值。这些值不是有效的跨语言载荷。

创建原生模式 Fory 实例

创建 Fory 时设置 xlang=False

import pyfory
fory = pyfory.Fory(xlang=False, ref=False, strict=True)

对于已注册且可信的类型范围,请保持 strict=True。仅当原生模式载荷需要函数、本地类或由归约钩子重建的对象等动态 Python 类型时,才使用 strict=False

常见用法

import pyfory

fory = pyfory.Fory(xlang=False, ref=True, strict=False)

data = fory.dumps({"name": "Alice", "age": 30, "scores": [95, 87, 92]})
print(fory.loads(data))

from dataclasses import dataclass

@dataclass
class Person:
name: str
age: int

person = Person("Bob", 25)
data = fory.dumps(person)
print(fory.loads(data)) # Person(name='Bob', age=25)

需要 pickle 风格 API 时使用 dumps/loads;在显式切换模式的代码中需要与跨语言 API 形式保持一致时,则使用 serialize/deserialize

安全与动态类型

原生模式可以重建会在反序列化期间执行导入和构造逻辑的 Python 对象。应像对待不可信 pickle 字节一样对待不可信原生模式字节。

  • 反序列化只应包含已注册或内置类型的数据时,保持 strict=True
  • 仅对需要动态 Python 类或函数的可信载荷使用 strict=False
  • 需要动态类型但仍应限制可接受类型范围时,提供 policy= 反序列化策略。
  • 不要将跨语言/原生模式选择用作安全控制。应根据载荷来源应用严格模式、策略、注册和资源限制。

Python 专用值与钩子

可调用对象和类型值参见函数、类与方法,归约、状态、构造和 pickle/cloudpickle 迁移参见序列化钩子

引用与循环

需要往返保留对象标识、共享引用或循环时,启用 ref=True

import pyfory

fory = pyfory.Fory(xlang=False, ref=True, strict=True)

node = {}
node["self"] = node
data = fory.dumps(node)
decoded = fory.loads(data)
assert decoded["self"] is decoded

不需要保留对象标识的值形载荷应禁用引用跟踪,这样可以减小载荷并简化热点路径。

带外缓冲区

对于大型二进制载荷和由外部内存支撑的数据结构,Python 原生模式可以使用 pickle 协议 5 风格的带外缓冲区:

import pickle
import pyfory

data = b"Large binary data"
pickle_buffer = pickle.PickleBuffer(data)

buffer_objects = []
fory = pyfory.Fory(xlang=False, ref=True, strict=False)
serialized = fory.dumps(pickle_buffer, buffer_callback=buffer_objects.append)
buffers = [obj.getbuffer() for obj in buffer_objects]
decoded = fory.loads(serialized, buffers=buffers)
assert bytes(decoded.raw()) == data

载荷只在 Python 中使用,并且大型缓冲区应避免额外复制时,使用此方式。参见带外序列化

原生模式与跨语言模式对比

需求使用原生序列化使用跨语言序列化
仅限 Python 的载荷可选
非 Python 读取端或写入端
函数、lambda、本地类
__reduce__ / __getstate__ 对象钩子
替代 Pickle/cloudpickle
跨语言可移植类型映射

性能对比

import pyfory
import pickle
import timeit

fory = pyfory.Fory(xlang=False, ref=True, strict=False)

obj = {f"key{i}": f"value{i}" for i in range(10000)}
print(f"Fory: {timeit.timeit(lambda: fory.dumps(obj), number=1000):.3f}s")
print(f"Pickle: {timeit.timeit(lambda: pickle.dumps(obj), number=1000):.3f}s")

故障排除

其他语言无法读取载荷

写入端正在使用原生序列化。请使用 xlang=True 重新构建,在每个对等端注册可移植 Schema,并避免使用 lambda 或本地类等仅限 Python 的值。

动态类或函数反序列化失败

对可信载荷使用 strict=False;只应接受选定动态类型时,提供反序列化 policy=

循环无法往返保留

创建 Fory 实例时设置 ref=True

值依赖 pickle 钩子

保持载荷使用原生模式。跨语言模式不会执行 Python __reduce____reduce_ex____getstate____setstate__ 对象重建钩子。

相关主题