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

3

我正在尝试理解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加载结果的目的是什么?(反之亦然)

1个回答

5
使用 marshal 发送的对象是一个非常特定的类型。它是一个代码对象,只需要支持该类型。这是 marshal 模块所设计处理的类型。另一方面,返回值可以是任何类型,取决于 func 函数的返回值。相比之下,pickle 模块有更通用的协议,可以序列化许多不同类型的对象,因此有很大的可能它也支持返回值。
虽然你可以使用 pickle 在传送数据项目时,但对于传递代码对象而言,marshal 模块的输出会更加紧凑和高效,因为 pickle 只是包裹它。如果你尝试使用 marshalpickle(在 Python 2 中默认为协议零)同时将同一个代码对象进行转储,那么你将会看到 marshal 的输出被包含在 pickle 的输出中!
总之,marshal 模块用于发送代码,因为只有代码对象需要发送,并且这是一种较低级别的序列化,有时候效率更高。而返回值则使用 pickle 发送,因为程序无法预测其类型,并且 pickle 可以序列化比 marshal 更为复杂的值,代价是一些额外的复杂性和(有时)序列化大小的增加。

Pickle无法序列化代码对象。它可以通过名称序列化函数,但只能在接收端定义相应的函数。因此,如果您想序列化可执行代码,您需要使用marshal;对于其他情况,您可能应该使用pickle。 - Evan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接