Pickle 能够序列化 .ipynb 中的代码对象,但无法在 .py 中执行此操作。为什么这样?

Pickle is able to serialize code object in .ipynb, but fails to do it in .py. Why so?

试图迈出 Python 编程奇妙世界的第一步,我在一开始就跌跌撞撞。

我正在尝试为 Python 上的 Python 字节码实现一个虚拟机(出于教育目的)。因此,将字节码的制作过程与 运行 分开——似乎是个好主意。我想在反序列化之前将代码对象序列化到文件中,然后 运行 它在一个单独的脚本中。

这是序列化程序的代码:

import pickle

code_obj = compile("print('Hi!')", "<string>", "exec", dont_inherit=True)
ps = pickle.dumps(code_obj)
with open(r"./hi.dump", "wb") as f_dump:
    f_dump.write(ps)

它是这样崩溃的:

Traceback (most recent call last):
  File "d:/Syncthing/tragicon/_x/Python/pyro/dumper.py", line 4, in <module>
    ps = pickle.dumps(code_obj)
TypeError: cannot pickle 'code' object

嗯,或许应该只是以代码对象不适合pickle.dumps为前提。或者可能不是,因为没有问题 运行 Jupiter Notebook 中的代码完全相同:

import pickle

code_obj = compile("print('Hi!')", "<string>", "exec", dont_inherit=True)
ps = pickle.dumps(code_obj)
with open(r"./hi.dump", "wb") as f_dump:
    f_dump.write(ps)
ps

输出:

b'\x80\x04\x95u\x00\x00\x00\x00\x00\x00\x00\x8c\x12ipykernel.codeutil\x94\x8c\tcode_ctor\x94\x93\x94(K\x00K\x00K\x00K\x00K\x02K@C\x0ce\x00d\x00\x83\x01\x01\x00d\x01S\x00\x94\x8c\x03Hi!\x94N\x86\x94\x8c\x05print\x94\x85\x94)\x8c\x08<string>\x94\x8c\x08<module>\x94K\x01C\x00\x94))t\x94R\x94.'

我很容易认为不可能用 pickle 序列化代码对象,但这种行为上的矛盾让我感到困惑。

或者有更好的序列化方法。

我很乐意就此获得一些建议。

看来找到答案了。

查询显示,Jupiter Notebook 将模块 ipyparallel.serialize.codeutil 附加到其运行时(在某些版本中为 ipykernel.codeutil)。这个模块唯一能做的就是允许你 pickle 代码对象。

因此,为了使我的初始代码正常工作,我应该将 import ipyparallel.serialize.codeutil 放在脚本的顶部(可能还需要 pip install ipyparallel)。