跳到主要内容
版本:dev

基础序列化

本页介绍 Python 跨语言模式快速入门。pyfory.Fory() 默认使用支持兼容 Schema 演进的跨语言模式;示例会显式设置 xlang=True,以清楚表达模式选择。

基础对象序列化

使用简单的 API 序列化和反序列化 Python 对象:

import pyfory

fory = pyfory.Fory(xlang=True)

# Serialize xlang-compatible values
data = fory.dumps({"name": "Alice", "age": 30, "scores": [95, 87, 92]})

# Deserialize back to Python object
obj = fory.loads(data)
print(obj) # {'name': 'Alice', 'age': 30, 'scores': [95, 87, 92]}

注意dumps()/loads()serialize()/deserialize() 的别名。两组 API 完全相同,可选择更直观的一组。

自定义类序列化

使用 dataclass 和类型注解构建稳定的跨语言载荷:

import pyfory
from dataclasses import dataclass
from typing import List, Dict

@dataclass
class Person:
name: str
age: pyfory.Int32
scores: List[pyfory.Int32]
metadata: Dict[str, str]

fory = pyfory.Fory(xlang=True, ref=True)
fory.register(Person, name="example.Person")
person = Person("Bob", 25, [88, 92, 85], {"team": "engineering"})
data = fory.serialize(person)
result = fory.deserialize(data)
print(result) # Person(name='Bob', age=25, ...)

引用跟踪与循环引用

载荷使用跨语言兼容类型时,可以安全处理重复引用:

import pyfory

f = pyfory.Fory(xlang=True, ref=True)

shared = ["shared"]
value = [shared, shared]

data = f.serialize(value)
result = f.deserialize(data)
assert result[0] is result[1]

任意 Python 对象图、本地类、函数和方法应使用原生序列化

性能技巧

  1. 不需要时禁用 ref=True:引用跟踪存在开销
  2. 使用 type_id 而非 name:整数 ID 比字符串名称更快
  3. 复用 Fory 实例:创建一次,多次使用
  4. 启用 Cython:确保设置 ENABLE_FORY_CYTHON_SERIALIZATION=1
# Good: Reuse instance
fory = pyfory.Fory(xlang=True)
for obj in objects:
data = fory.dumps(obj)

# Bad: Create new instance each time
for obj in objects:
fory = pyfory.Fory(xlang=True) # Wasteful!
data = fory.dumps(obj)

跨语言互操作

以下内容说明默认 xlang 格式的跨语言类型映射、类型标识和互操作要求。

pyfory 支持跨语言对象图序列化,可以在 Python 中序列化数据,再由 Java、C++、Go、Rust、JavaScript/TypeScript、C#、Swift、Dart、Scala、Kotlin 或其他受支持语言进行反序列化。

Xlang 配置

Python 默认使用支持兼容 Schema 演进的跨语言模式。跨语言示例会显式设置该模式:

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

跨语言示例

Python(序列化端)

import pyfory
from dataclasses import dataclass

f = pyfory.Fory(xlang=True, ref=True)

# Register type for xlang compatibility
@dataclass
class Person:
name: str
age: pyfory.Int32

f.register(Person, name="example.Person")

person = Person("Charlie", 35)
binary_data = f.serialize(person)
# binary_data can now be sent to Java, Go, etc.

Java(反序列化端)

import org.apache.fory.*;

public class Person {
public String name;
public int age;
}

Fory fory = Fory.builder()
.withXlang(true)
.withRefTracking(true)
.build();

fory.register(Person.class, "example.Person");
Person person = (Person) fory.deserialize(binaryData);

Rust(反序列化端)

use fory::Fory;
use fory::ForyStruct;

#[derive(ForyStruct)]
struct Person {
name: String,
age: i32,
}

let mut fory = Fory::builder().xlang(true).build();

fory.register_by_name::<Person>("example.Person");
let person: Person = fory.deserialize(&binary_data)?;

跨语言类型注解

使用 pyfory 类型注解显式指定跨语言类型映射:

请直接在 Python 类型注解中使用这些标记。字段值仍是普通 Python intfloat 值,Fory 会使用指定的跨语言数字宽度和编码对其进行序列化。

from dataclasses import dataclass
from typing import Dict, List
import pyfory

@dataclass
class TypedData:
int_value: pyfory.Int32 # 32-bit integer
long_value: pyfory.Int64 # 64-bit integer
float_value: pyfory.Float32 # 32-bit float
double_value: pyfory.Float64 # 64-bit float
values: Dict[pyfory.Int32, List[pyfory.Int64]]

嵌套集合注解是字段 Schema 的一部分。兼容模式读取会根据远程 Schema 元数据消费字节,并且只有在解码值能够安全满足本地 Schema 时才进行赋值。

低精度类型

pyfory.Float16pyfory.BFloat16 是为跨语言低精度字段保留的注解标记,并非值包装类;标量值会反序列化为原生 Python float

稠密低精度数组使用具有类列表序列行为的公共稠密包装器。通过 pyfory.Float16Array.from_values([...])pyfory.BFloat16Array.from_values([...]) 从 Python 数字值构造。仅在使用 from_buffer(...)to_buffer() 处理已经需要的打包小端序 uint16 存储,并希望走原始缓冲区快速路径时采用该方式。

类型映射

Python 标记/载体JavaRustGo
strStringStringstring
intlongi64int64
pyfory.Int32inti32int32
pyfory.Int64longi64int64
floatdoublef64float64
pyfory.Float32floatf32float32
pyfory.Float16Float16Float16float16.Float16
pyfory.BFloat16BFloat16BFloat16bfloat16.BFloat16
pyfory.Float16ArrayFloat16ListVec<Float16>[]float16.Float16
pyfory.BFloat16ArrayBFloat16ListVec<BFloat16>[]bfloat16.BFloat16
listListVec[]T
dictMapHashMapmap[K]V

列表与稠密数组

Python List[T] 映射到 Fory list<T>。仅当需要使用 pyfory.Array[T]pyfory.NDArray[T]pyfory.PyArray[T] 表示稠密一维 array<T> Schema 时,才采用这些注解。

Fory SchemaPython 注解和默认载体
list<int32>List[pyfory.Int32]
array<bool>pyfory.Array[bool] -> BoolArray
array<int8>pyfory.Array[pyfory.Int8] -> Int8Array
array<int16>pyfory.Array[pyfory.Int16] -> Int16Array
array<int32>pyfory.Array[pyfory.Int32] -> Int32Array
array<int64>pyfory.Array[pyfory.Int64] -> Int64Array
array<uint8>pyfory.Array[pyfory.UInt8] -> UInt8Array
array<uint16>pyfory.Array[pyfory.UInt16] -> UInt16Array
array<uint32>pyfory.Array[pyfory.UInt32] -> UInt32Array
array<uint64>pyfory.Array[pyfory.UInt64] -> UInt64Array
array<float16>pyfory.Array[pyfory.Float16] -> Float16Array
array<bfloat16>pyfory.Array[pyfory.BFloat16] -> BFloat16Array
array<float32>pyfory.Array[pyfory.Float32] -> Float32Array
array<float64>pyfory.Array[pyfory.Float64] -> Float64Array

pyfory.*Array 包装器接受 pyfory.Float32Array([1, 2, 3]) 等可迭代对象构造器,并在自有稠密存储上提供类列表序列行为。

pyfory.Array[T]pyfory.NDArray[T]pyfory.PyArray[T] 都描述相同的 Fory array<T> Schema,区别仅在于 Python 载体契约:

Python 字段注解该字段接受的值反序列化载体
pyfory.Array[T]pyfory.*Array, numpy.ndarray, array.array, listpyfory.*Array
pyfory.NDArray[T]numpy.ndarraynumpy.ndarray
pyfory.PyArray[T]Python array.arrayPython array.array

在兼容模式下,只要两个注解都归一为相同的 Fory array<T> Schema,写入端和读取端就可以为同名字段使用不同的 Python 载体。例如,声明为 pyfory.Array[pyfory.Int32] 的写入端字段,可以由匹配字段声明为 pyfory.NDArray[pyfory.Int32] 的 Python 类读取,读取端会得到 NumPy int32 ndarray。反向模式同样适用于 pyfory.PyArray[T];该名称始终表示 Python array.array

PyArrow 是独立的行式/列式格式能力,不是 pyfory.PyArray 载体。使用 pyfory.format.from_arrow_schema(...)pyfory.format.to_arrow_schema(...) 在 PyArrow Schema 与 Fory Row Format Schema 之间转换。

与 Python 原生模式的区别

二进制协议和 API 与 pyfory 的 Python 原生模式相似,但 Python 原生模式可以序列化任意 Python 对象,包括全局函数、本地函数、lambda、本地类,以及使用 __getstate__/__reduce__/__reduce_ex__ 自定义序列化的类型;这些值在跨语言模式中不允许使用

规范与参考

相关指南

读取 Java 文件示例

import pyfory
from dataclasses import dataclass

@dataclass
class Person:
name: str
age: pyfory.Int32

fory = pyfory.Fory(xlang=True)
fory.register_type(Person, name="example.Person")

with open("person.bin", "rb") as f:
data = f.read()

person = fory.deserialize(data)
print(f"Name: {person.name}, Age: {person.age}")
# Output: Name: Alice, Age: 30

内置值

import pyfory
import numpy as np

fory = pyfory.Fory(xlang=True)
object_list = [True, False, "str", -1.1, 1,
np.full(100, 0, dtype=np.int32), np.full(20, 0.0, dtype=np.double)]
data = fory.serialize(object_list)
# bytes can be deserialized by other languages
new_list = fory.deserialize(data)
object_map = {"k1": "v1", "k2": object_list, "k3": -1}
data = fory.serialize(object_map)
# bytes can be deserialized by other languages
new_map = fory.deserialize(data)
print(new_map)

自定义值

from dataclasses import dataclass
from typing import List, Dict, Any
import pyfory, array


@dataclass
class SomeClass1:
f1: Any
f2: Dict[pyfory.Int8, pyfory.Int32]


@dataclass
class SomeClass2:
f1: Any = None
f2: str = None
f3: List[str] = None
f4: Dict[pyfory.Int8, pyfory.Int32] = None
f5: pyfory.Int8 = None
f6: pyfory.Int16 = None
f7: pyfory.Int32 = None
# int type will be taken as `pyfory.Int64`.
# use `pyfory.Int32` for type hint if peer uses more narrow type.
f8: int = None
f9: pyfory.Float32 = None
# float type will be taken as `pyfory.Float64`
f10: float = None
f11: pyfory.Array[pyfory.Int16] = None
f12: List[pyfory.Int16] = None


if __name__ == "__main__":
f = pyfory.Fory(xlang=True)
f.register_type(SomeClass1, name="example.SomeClass1")
f.register_type(SomeClass2, name="example.SomeClass2")
obj1 = SomeClass1(f1=True, f2={-1: 2})
obj = SomeClass2(
f1=obj1,
f2="abc",
f3=["abc", "abc"],
f4={1: 2},
f5=2 ** 7 - 1,
f6=2 ** 15 - 1,
f7=2 ** 31 - 1,
f8=2 ** 63 - 1,
f9=1.0 / 2,
f10=1 / 3.0,
f11=array.array("h", [1, 2]),
f12=[-1, 4],
)
data = f.serialize(obj)
# bytes can be deserialized by other languages
print(f.deserialize(data))

共享引用与循环引用

from typing import Dict
import pyfory

class SomeClass:
f1: "SomeClass"
f2: Dict[str, str]
f3: Dict[str, str]

fory = pyfory.Fory(xlang=True, ref=True)
fory.register_type(SomeClass, name="example.SomeClass")
obj = SomeClass()
obj.f2 = {"k1": "v1", "k2": "v2"}
obj.f1, obj.f3 = obj, obj.f2
data = fory.serialize(obj)
# bytes can be deserialized by other languages
print(fory.deserialize(data))

相关主题