为什么要使用 marshal 转储和 pickle 加载?

Why to use marshal to dump and pickle to load?

我正在尝试了解 python2 库 remotely,它有助于通过 xmlrpc 远程 运行 编码。

在客户端,作者使用 marshal 转储对象,并使用 pickle 加载从服务器发回的结果:

def run(self, func, *args, **kwds):
        code_str = base64.b64encode(marshal.dumps(func.func_code))
        output = self.proxy.run(self.api_key, self.a_sync, code_str, *args, **kwds)
        return pickle.loads(base64.b64decode(output))

而在服务器端,他正在做相反的事情:

def run(self, api_key, a_sync, func_str, *args, **kwds):
        #... truncated code
        code = marshal.loads(base64.b64decode(func_str))
        func = types.FunctionType(code, globals(), "remote_func")
        #... truncated code
        output = func(*args, **kwds)
        output = base64.b64encode(pickle.dumps(output))
        return output

用 marshal 转储并用 pickle 加载结果的目的是什么? (反之亦然)

首先使用 marshal 发送的对象属于非常特殊的类型。它是一个代码对象,只需要支持该类型。这是 marshal 模块旨在处理的类型。另一方面,return 值可以是任何类型,它由 func 函数 return 的内容决定。 pickle 模块有一个更通用的协议,可以序列化许多不同类型的对象,因此它很有可能支持 return 值。

虽然您可以对在程序之间传递的两个数据项使用 pickle,但 marshal 模块的输出对于传递代码对象来说更加紧凑和高效,因为 pickle只是环绕它。如果您尝试使用 marshalpickle 转储相同的代码对象(使用协议零,Python 2 中的默认协议),您将在内部看到 marshal 的输出pickle!

的输出

所以总结一下,使用marshal模块发送代码,因为只需要发送个代码对象,而且是低级序列化,可以有时效率更高一点。 return 值使用 pickle 发回,因为程序无法预测它将是什么类型的对象,并且 pickle 可以序列化比 marshal 更复杂的值,以一些额外的复杂性和(有时)序列化的大小为代价。