You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CPython中PyObject序列化跨解释器共享及实现疑问

Python对象跨解释器共享与序列化问题解答

问题背景

我尝试将PyObject以字节形式写入文件,再通过另一个Python解释器实例读取访问。核心目标是将PyObject放入共享内存,供其他Python虚拟机实例访问。由于Linux下共享内存可通过文件描述符操作,因此需将PyObject转为字节存储到文件,再由其他解释器读取。

为此编写了如下C扩展函数,接收Python整数对象,将其写入文件后再读取返回:

PyObject* func(PyObject* self, PyObject* args)
{
    // Writing the Object to a file                                   //1
    const char* const path = "file.bin";                              //2
    FILE* file = fopen(path,"wb");                                    //3
    fwrite( args, sizeof(PyLongObject) , 1 , file );                  //4
    fclose( file );                                                   //5

    //Reading the Object from the file
    file = fopen(path,"rb");                                          //6
    PyObject* contents = NULL;                                        //7
    contents = PyMem_Malloc( sizeof(PyLongObject) );                  //8  
    fread( contents, sizeof(PyLongObject) , 1 , file );               //9
    fclose( file );                                                   //10
    
    //Forcing the REFCOUNT to 1
    Py_SET_REFCNT(contents,1);                                        //11

    return contents;                                                  //12
} 

该函数传入整数时可正常返回对应值,但存在以下疑问:

疑问解答

  • 疑问1:第4行写入了对象内的所有指针,在其他解释器中这些地址无效,此理解是否正确?
    完全正确。PyLongObject内部包含指针(比如指向数字数据的缓冲区指针),这些地址是当前Python解释器进程虚拟地址空间内的有效地址,其他解释器进程拥有独立的虚拟地址空间,这些地址在另一个进程里要么指向无效内存,要么指向无关数据,跨进程场景下必然无法正常工作。当前测试能正常运行是因为在同一个解释器进程内读写,地址空间一致。

  • 疑问2:第8行使用PyMem_Malloc分配内存,是否需要手动free?还是Python虚拟机在引用计数归0时自动释放?
    不需要手动调用free。PyMem_Malloc属于Python的内存分配器,当你将这块内存包装成PyObject并设置引用计数后,Python的垃圾回收机制会在引用计数归0时自动通过PyMem_Free释放内存。但要确保这块内存被当作合法的Python对象管理,否则可能引发内存泄漏。

  • 疑问3:第11行强制将新对象引用计数设为1(因原对象引用计数可能大于1),此操作是否正确?
    在当前场景下是正确的。通过fread得到的是原对象的内存副本,它不属于当前解释器的对象管理体系,引用计数是原对象的数值,必须手动设置为1,这样Python解释器才能正确跟踪该对象的生命周期,避免提前释放或内存泄漏。

  • 疑问4:若原对象(args)引用计数归0,是否会导致复制到contents中的指针失效?但测试删除原对象后contents仍正常,原因是什么?
    不会失效。你在第4行是把PyLongObject的整个内存内容复制到文件,再读回成一块新的独立内存区域。contents是完全独立的内存副本,和原对象args的内存没有关联。原对象被回收只是释放自身内存,不会影响已复制出的contents内存。测试中del(a)只是减少原整数对象的引用计数,计数归0后原对象销毁,但contents是独立副本,因此依然能正常访问。

  • 疑问5:将Python对象序列化到文件并跨解释器读取的正确方法是什么?
    直接内存拷贝PyObject的方式仅适合同一进程内,跨解释器/进程必须使用序列化,将对象的数据内容转换为平台无关的字节流,而非拷贝对象的内存结构。常见的正确做法:

    • 使用Python标准库的pickle:这是Python原生序列化工具,支持大部分内置对象和自定义对象,跨进程/解释器直接读写序列化后的字节流即可。
    • 针对简单类型(如整数、字符串),手动转换为文本或固定格式字节(比如将整数转成4字节/8字节的大端/小端字节序),另一端再解析还原。
    • 结合Linux共享内存使用:先将对象序列化为字节数据写入共享内存,另一端读取字节数据后反序列化还原为Python对象。
    • 自定义C扩展可使用PyMarshal模块API(如PyMarshal_WriteObjectToFile和PyMarshal_ReadObjectFromFile),这是Python内部的轻量级序列化接口,但支持的对象类型有限。

内容的提问来源于stack exchange,提问作者daniel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 05:27:32