Сериализация объекта пены в python
Хорошо, я работаю над улучшением работы с python, поэтому я не уверен, что это правильный способ, чтобы начать то, что я делаю для начала, но вот моя текущая проблема...
Мне нужно получить некоторую информацию с помощью SOAP-метода и использовать часть информации сейчас, но сохранить весь результат для будущего использования (нам нужно использовать сервис как можно меньше). Глядя на лучший способ доступа к сервису, я подумал, что пенопласт - это путь, и это было просто и работало как шарм, чтобы получить данные. Но теперь я хочу как-то сохранить результат, желательно сериализованный/в базе данных, чтобы потом вытащить его и использовать его одинаково.
Какой лучший способ сделать это, похоже, что pickle/json не вариант? Спасибо!
Обновление
Читая верный ответ на Как я могу расчехлять результаты пенообразования? дает мне лучшее представление о том, почему это не вариант, я думаю, что я застрял, воссоздавая базовый объект с информацией, которая мне нужна?
Ответы
Ответ 1
Да, я подтверждаю объяснение, которое я дал в ответе, на который вы ссылаетесь, - динамически сгенерированные классы нелегко разбираются (и не могут быть легко сериализованы), вам нужно извлечь всю информацию о состоянии, рассолить это состояние и восстановить сложную sudsobject при поиске, если вы действительно настаиваете на его использовании; -).
Ответ 2
Я использовал следующий подход для преобразования объекта Suds в JSON:
from suds.sudsobject import asdict
def recursive_asdict(d):
"""Convert Suds object into serializable format."""
out = {}
for k, v in asdict(d).iteritems():
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))
Ответ 3
Я сделал реализацию фиктивного класса для Object intance 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)
Надеюсь, это поможет.
Ответ 4
Вот что я придумал, прежде чем исследовать и найти этот ответ. Это на самом деле хорошо работает для меня при сложных реакциях пенообразования, а также на других объектах, таких как __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__
.
Я провел тест 100 раз на объекте вывода сложных пеной, это время выполнения решений составляло 0,04-0,052 секунды (0,045724287 в среднем). В то время как решение recursive_asdict
выше находилось в диапазоне от 0,082 до 0,102 секунды, что почти удваивалось (0,0829765582 в среднем).
Затем я вернулся к чертежной доске и повторно выполнил эту функцию, чтобы получить больше производительности, и ей не нужен импорт datetime
. Я использовал использование атрибута __keylist__
, поэтому это не будет работать на других объектах, таких как __builtins__
, но отлично работает для вывода объекта suds:
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 среднее значение), что почти в 4 раза быстрее, чем решение recursive_asdict
.
Ответ 5
Я обновил пример 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 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
Ответ 6
Решения, рассмотренные выше, теряют ценную информацию о именах классов - это может быть полезно в некоторых библиотеках, таких как DFP-клиент https://github.com/googleads/googleads-python-lib, где entity типы могут быть закодированы в динамически сгенерированных именах классов (т.е. TemplateCreative/ ImageCreative)
Здесь решение, которое я использовал, сохраняет имена классов и восстанавливает объекты с сериализацией dict-serialized без потери данных (кроме suds.sax.text.Text, которые будут преобразованы в обычные объекты Unicode и, возможно, некоторые другие типы Я не столкнулся)
from suds.sudsobject import asdict, Factory as SudsFactory
def suds2dict(d):
"""
Suds object serializer
Borrowed from https://stackoverflow.com/info/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)