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

Ody*_*see 3 python marshalling pickle

我试图远程了解 python2 库,这有助于通过 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))
Run Code Online (Sandbox Code Playgroud)

而在服务器端,他则采取相反的方式:

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
Run Code Online (Sandbox Code Playgroud)

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

Blc*_*ght 5

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

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

总而言之,该marshal模块用于发送代码,因为需要发送代码对象,并且它是较低级别的序列化,有时可能会更高效一些。返回值被发送回来,pickle因为程序无法预测它将是什么类型的对象,并且pickle可以序列化比可以序列化更复杂的值marshal,但代价是增加一些额外的复杂性和(有时)序列化的大小。