Как обобщенно сериализовать и десериализовать объекты из словарей - PullRequest
0 голосов
/ 18 июня 2019

При написании небольшого сервиса я обнаружил, что хотел бы построить объекты Python из словарей, а затем «сериализовать» их обратно в словари.

В настоящее время моя проблема заключается в том, что я использую смесьitemgetter и vars.Не дает мне ожидаемого результата.Конкретнее:

from operator import itemgetter

class Object(object):
    def __init__(self, foo: str, bar: int):
        self.foo, self.bar = foo, bar

    @staticmethod
    def from_dict(dictionary):
        attributes = ["foo", "bar"]
        return Object(*itemgetter(*attributes)(dictionary))

Итак, у нас есть статический метод с именем from_dict, который должен взять словарь и вернуть объект Object.

Это поведение, которое я хотел бы:

>> d = {"foo": "frobulate", "bar": 42}
>> obj = Object.from_dict(d)
>> vars(obj)
{"foo": "frobulate", "bar": 42}

Однако вместо этого я получаю:

>> d = {"foo": "frobulate", "bar": 42}
>> obj = Object.from_dict(d)
>> vars(obj)
{"foo": ("frobulate",), "bar": (42,)}

Где атрибуты задаются как кортежи из одного элемента.

Есть ли чистый Python способ получить это, дайте мне поведение, которое я ожидаю?

Я пытаюсь избежать дублирования, как:

class Object(object):
    ...
    @staticmethod
    def from_dict(dictionary):
        return Object(foo=dictionary["foo"],bar=dictionary["bar"])

В конце концов я быхотел бы иметь универсальный метод, который принимает класс cls и может анализировать любой объект из словаря:

def parse_object_from_dict(cls, attributes, dictionary):
    return cls(*itemgetter(*attributes)(dictionary))

, который можно использовать как:

>> foo = parse_object_from_dict(Foo, ["a", "b"], {"a": 42, "b": 42})
>> obj = parse_object_from_dict(Object, ["foo", "bar"], {"foo": 42, "bar": 42}

и иметь атрибутыустановить как ожидалось (не как одноэлементные кортежи!).

Возможно ли это?Или даже хорошая идея?

Ответы [ 2 ]

2 голосов
/ 18 июня 2019

Для того, что вы описываете, вы должны использовать метод класса. Вы требуете, чтобы входной словарь имел те же ключи, что и аргументы в функции __init__, но помимо этого вы просто выдвигаете пары ключ-значение к объекту.

class Object:
    def __init__(self, foo, bar):
        self.foo = foo
        self.bar = bar

    @classmethod
    def from_dict(cls, d):
        ob = cls(**d)
        for k,v in d.items():
            setattr(ob, str(k), v)    
        return ob
1 голос
/ 18 июня 2019

Один из способов сделать это - setattr

class Object(object):
    def __init__(self, foo=None, bar=None):
        self.foo = foo
        self.bar = bar

    @staticmethod
    def from_dict(dic):
        obj = Object()
        [setattr(obj, attr, dic[attr]) for attr in ["foo", "bar"]]
        return obj

Другой способ - использование **, но это может быть неаккуратным способом выполнения действий и может выдавать ошибки, если в вашем словаре есть дополнительные ключи.

class Object(object):
    def __init__(self, foo, bar):
        self.foo = foo
        self.bar = bar

    @staticmethod
    def from_dict(dic):
        return Object(**dic)

data = {'foo': 'a', 'bar': 2}
o = Object.from_dict(data)

data = {'foo': 'a', 'bar': 2, 'baz': 3}
o = Object.from_dict(data) # this will throw an error due to the 'baz' key

Один из последних способов сделать это, который, вероятно, является самым чистым, состоит в том, чтобы попытаться использовать Marshmallow, который будет выполнять сериализацию и десериализацию для вас.

https://marshmallow.readthedocs.io/en/3.0/api_reference.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...