好吧,我正在努力更好地使用 python,所以我不确定这是开始我正在做的事情的正确方法,但这是我当前的问题......
我需要通过 SOAP 方法获取一些信息,并且现在只使用部分信息,但存储整个结果以供将来使用(我们需要尽可能少地使用该服务)。 寻找访问该服务的最佳方式,我认为泡沫是最好的方式,而且它很简单,并且可以像魅力一样获取数据。 但现在我想以某种方式保存结果,最好是序列化/保存在数据库中,这样我就可以稍后将其取出并使用它。
最好的方法是什么,看起来 pickle/json 不是一个选择? 谢谢!
更新 阅读最上面的答案如何腌制泡沫结果?让我更好地了解为什么这不是一个选项,我想我一直在重新创建一个带有我需要的信息的基本对象?
我一直在使用以下方法将 Suds 对象转换为 JSON:
from suds.sudsobject import asdict
def recursive_asdict(d):
"""Convert Suds object into serializable format."""
out = {}
for k, v in asdict(d).items():
if hasattr(v, '__keylist__'):
out[k] = recursive_asdict(v)
elif isinstance(v, list):
out[k] = []
for item in v:
if hasattr(item, '__keylist__'):
out[k].append(recursive_asdict(item))
else:
out[k].append(item)
else:
out[k] = v
return out
def suds_to_json(data):
return json.dumps(recursive_asdict(data))
是的,我确认了我在您提到的答案中给出的解释——动态生成的类不容易picklable(也不容易序列化),您需要提取所有状态信息,pickle that状态,并重建棘手的问题如果您确实坚持使用它,则检索时会使用 sudsobject;-)。
这是我在研究和找到这个答案之前想到的。这实际上对我来说对复杂的泡沫响应以及其他对象(例如
__builtins__
)很有效,因为解决方案与泡沫无关:
import datetime
def object_to_dict(obj):
if isinstance(obj, (str, unicode, bool, int, long, float, datetime.datetime, datetime.date, datetime.time)):
return obj
data_dict = {}
try:
all_keys = obj.__dict__.keys() # vars(obj).keys()
except AttributeError:
return obj
fields = [k for k in all_keys if not k.startswith('_')]
for field in fields:
val = getattr(obj, field)
if isinstance(val, (list, tuple)):
data_dict[field] = []
for item in val:
data_dict[field].append(object_to_dict(item))
else:
data_dict[field] = object_to_dict(val)
return data_dict
这个解决方案有效并且实际上更快。它也适用于没有
__keylist__
属性的对象。
我在复杂的 suds 输出对象上运行基准测试 100 次,该解决方案的运行时间为 0.04 到 0.052 秒(平均值为 0.045724287)。而上面的
recursive_asdict
解决方案的运行时间为 0.082 到 0.102 秒,几乎翻倍(平均值为 0.0829765582)。
然后我回到绘图板并重新完成该功能以获得更多性能,并且它不需要
datetime
导入。我利用了 __keylist__
属性,因此这不适用于其他对象,例如 __builtins__
,但对于泡沫对象输出效果很好:
def fastest_object_to_dict(obj):
if not hasattr(obj, '__keylist__'):
return obj
data = {}
fields = obj.__keylist__
for field in fields:
val = getattr(obj, field)
if isinstance(val, list): # tuple not used
data[field] = []
for item in val:
data[field].append(fastest_object_to_dict(item))
else:
data[field] = fastest_object_to_dict(val)
return data
运行时间为 0.18 - 0.033 秒(平均值为 0.0260889721),比
recursive_asdict
解决方案快了近 4 倍。
我为 suds 的对象实例实现了一个虚拟类,然后能够序列化。 FakeSudsInstance 的行为类似于原始 Suds 对象实例,请参见下文:
from suds.sudsobject import Object as SudsObject
class FakeSudsNode(SudsObject):
def __init__(self, data):
SudsObject.__init__(self)
self.__keylist__ = data.keys()
for key, value in data.items():
if isinstance(value, dict):
setattr(self, key, FakeSudsNode(value))
elif isinstance(value, list):
l = []
for v in value:
if isinstance(v, list) or isinstance(v, dict):
l.append(FakeSudsNode(v))
else:
l.append(v)
setattr(self, key, l)
else:
setattr(self, key, value)
class FakeSudsInstance(SudsObject):
def __init__(self, data):
SudsObject.__init__(self)
self.__keylist__ = data.keys()
for key, value in data.items():
if isinstance(value, dict):
setattr(self, key, FakeSudsNode(value))
else:
setattr(self, key, value)
@classmethod
def build_instance(cls, instance):
suds_data = {}
def node_to_dict(node, node_data):
if hasattr(node, '__keylist__'):
keys = node.__keylist__
for key in keys:
if isinstance(node[key], list):
lkey = key.replace('[]', '')
node_data[lkey] = node_to_dict(node[key], [])
elif hasattr(node[key], '__keylist__'):
node_data[key] = node_to_dict(node[key], {})
else:
if isinstance(node_data, list):
node_data.append(node[key])
else:
node_data[key] = node[key]
return node_data
else:
if isinstance(node, list):
for lnode in node:
node_data.append(node_to_dict(lnode, {}))
return node_data
else:
return node
node_to_dict(instance, suds_data)
return cls(suds_data)
现在,在肥皂水呼叫之后,例如下面:
# Now, after a suds call, for example below
>>> import cPickle as pickle
>>> suds_intance = client.service.SomeCall(account, param)
>>> fake_suds = FakeSudsInstance.build_instance(suds_intance)
>>> dumped = pickle.dumps(fake_suds)
>>> loaded = pickle.loads(dumped)
希望有帮助。
上面建议的解决方案丢失了有关类名称的宝贵信息 - 它在 DFP 客户端等某些库中可能很有价值 https://github.com/googleads/googleads-python-lib 其中实体类型可能在动态生成的类中进行编码名称(即 TemplateCreative/ImageCreative)
这是我使用的解决方案,它保留类名并恢复字典序列化对象而不会丢失数据(除了 suds.sax.text.Text ,它将被转换为常规 unicode 对象,也许还有一些我没有遇到过的其他类型)
from suds.sudsobject import asdict, Factory as SudsFactory
def suds2dict(d):
"""
Suds object serializer
Borrowed from https://stackoverflow.com/questions/2412486/serializing-a-suds-object-in-python/15678861#15678861
"""
out = {'__class__': d.__class__.__name__}
for k, v in asdict(d).iteritems():
if hasattr(v, '__keylist__'):
out[k] = suds2dict(v)
elif isinstance(v, list):
out[k] = []
for item in v:
if hasattr(item, '__keylist__'):
out[k].append(suds2dict(item))
else:
out[k].append(item)
else:
out[k] = v
return out
def dict2suds(d):
"""
Suds object deserializer
"""
out = {}
for k, v in d.iteritems():
if isinstance(v, dict):
out[k] = dict2suds(v)
elif isinstance(v, list):
out[k] = []
for item in v:
if isinstance(item, dict):
out[k].append(dict2suds(item))
else:
out[k].append(item)
else:
out[k] = v
return SudsFactory.object(out.pop('__class__'), out)
我更新了
recursive_asdict
示例上面以与python3兼容(items
而不是iteritems
)。
from suds.sudsobject import asdict
from suds.sax.text import Text
def recursive_asdict(d):
"""
Recursively convert Suds object into dict.
We convert the keys to lowercase, and convert sax.Text
instances to Unicode.
Taken from:
https://stackoverflow.com/a/15678861/202168
Let's create a suds object from scratch with some lists and stuff
>>> from suds.sudsobject import Object as SudsObject
>>> sudsobject = SudsObject()
>>> sudsobject.Title = "My title"
>>> sudsobject.JustAList = [1, 2, 3]
>>> sudsobject.Child = SudsObject()
>>> sudsobject.Child.Title = "Child title"
>>> sudsobject.Child.AnotherList = ["4", "5", "6"]
>>> childobject = SudsObject()
>>> childobject.Title = "Another child title"
>>> sudsobject.Child.SudObjectList = [childobject]
Now see if this works:
>>> result = recursive_asdict(sudsobject)
>>> result['title']
'My title'
>>> result['child']['anotherlist']
['4', '5', '6']
"""
out = {}
for k, v in asdict(d).items():
k = k.lower()
if hasattr(v, '__keylist__'):
out[k] = recursive_asdict(v)
elif isinstance(v, list):
out[k] = []
for item in v:
if hasattr(item, '__keylist__'):
out[k].append(recursive_asdict(item))
else:
out[k].append(
item.title() if isinstance(item, Text) else item)
else:
out[k] = v.title() if isinstance(v, Text) else v
return out
我喜欢这样。我们自己不进行迭代,是python在转换为字符串时进行迭代
class Ob:
def __init__(self, J) -> None:
self.J = J
def __str__(self):
if hasattr(self.J, "__keylist__"):
self.J = {key: Ob(value) for key, value in dict(self.J).items()}
if hasattr(self.J, "append"):
self.J = [Ob(data) for data in sefl.J]
return str(self.J)
result = Ob(result_soap)
我简化了@plaes 的答案(见下文)。 仍然递归,但更容易阅读和处理顶级列表/字典,以及混合字典/列表/suds 组合。
def pycast(o):
if hasattr(o, '__keylist__'):
return {k:pycast(v) for k,v in asdict(o).items()}
elif isinstance(o, dict):
return {k:pycast(v) for k,v in o.items()}
elif isinstance(o, list):
return [pycast(x) for x in o]
else:
return o