如何序列化suds结果?

9 投票
2 回答
4713 浏览
提问于 2025-04-15 18:40

为了在开发过程中避免频繁访问SOAP服务器,我想把结果缓存起来,这样就可以在运行其他代码时,不用每次都去查询服务器。

下面的代码让我在尝试保存一个suds结果时,遇到了一个错误:PicklingError: Can't pickle <class suds.sudsobject.AdvertiserSearchResponse at 0x03424060>: it's not found as suds.sudsobject.AdvertiserSearchResponse。我猜这是因为这些类是动态生成的。

import pickle
from suds.client import Client

client = Client(...)
result = client.service.search(...)

file = open('test_pickle.dat', 'wb')
pickle.dump(result, file, -1)
file.close()

如果我把-1这个协议版本从pickle.dump(result, file, -1)中去掉,我又遇到了一个不同的错误:

TypeError: a class that defines __slots__ without defining __getstate__ cannot be pickled

使用pickle保存数据是对的做法吗?我能让它工作吗?有没有更好的方法?

2 个回答

3

你正在对类对象本身进行“腌制”,而不是对类的实例对象进行“腌制”。如果类对象被重新创建,这样做就不行了。不过,只要类对象还存在,对类的实例进行“腌制”是可以的。

9

你现在看到的错误信息是在告诉你,你正在尝试对一些不能被“腌制”的实例进行处理。这是因为它们的类定义了 __slots__,但没有定义 __getstate__ 方法,这在你现在使用的旧版“腌制”协议中是个问题。

即使你修改了这些类也没用,因为你会遇到另一个问题——你已经正确识别出这个问题可能是由于动态生成的类造成的。所有的 pickle 协议都是通过“名称”来序列化类(和函数),这意味着它们必须在模块的顶层命名。而且,序列化一个实例确实需要先序列化它的类,否则你怎么能在以后重建这个实例呢?如果类不存在,那就没办法了!

所以你需要用其他方式来保存和加载你的数据,打破你现在对 suds.sudsobject 具体类的直接依赖,转而依赖一个接口(可以是正式定义的,也可以是通过鸭子类型定义的),这样在你访问 SOAP 服务器时可以用具体类实现,而在从文件加载数据时可以用更简单的“自制”类来实现。(表示实例状态的数据肯定可以用字典来表示,所以如果你真的想的话,可以通过 copy_reg 模块强行用 pickle 来处理,这个模块允许你自定义对象的序列化/反序列化协议,前提是你不能对它们的类进行侵入式修改,比如不能随便添加 __getstate__ 之类的东西——问题只会在这些对象之间有复杂的相互引用时出现。)

撰写回答