如何序列化suds结果?
为了在开发过程中避免频繁访问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 个回答
你正在对类对象本身进行“腌制”,而不是对类的实例对象进行“腌制”。如果类对象被重新创建,这样做就不行了。不过,只要类对象还存在,对类的实例进行“腌制”是可以的。
你现在看到的错误信息是在告诉你,你正在尝试对一些不能被“腌制”的实例进行处理。这是因为它们的类定义了 __slots__
,但没有定义 __getstate__
方法,这在你现在使用的旧版“腌制”协议中是个问题。
即使你修改了这些类也没用,因为你会遇到另一个问题——你已经正确识别出这个问题可能是由于动态生成的类造成的。所有的 pickle
协议都是通过“名称”来序列化类(和函数),这意味着它们必须在模块的顶层命名。而且,序列化一个实例确实需要先序列化它的类,否则你怎么能在以后重建这个实例呢?如果类不存在,那就没办法了!
所以你需要用其他方式来保存和加载你的数据,打破你现在对 suds.sudsobject
具体类的直接依赖,转而依赖一个接口(可以是正式定义的,也可以是通过鸭子类型定义的),这样在你访问 SOAP 服务器时可以用具体类实现,而在从文件加载数据时可以用更简单的“自制”类来实现。(表示实例状态的数据肯定可以用字典来表示,所以如果你真的想的话,可以通过 copy_reg
模块强行用 pickle 来处理,这个模块允许你自定义对象的序列化/反序列化协议,前提是你不能对它们的类进行侵入式修改,比如不能随便添加 __getstate__
之类的东西——问题只会在这些对象之间有复杂的相互引用时出现。)