В Python у меня есть класс данных, который содержит более десятка членов. Я использую его для создания текста, который я публикую в ElasticSearch .

Теперь я хочу получить dict от ElasticSearch и использовать его для инициализации класса данных.

Поскольку:

  1. Python не позволяет создавать второй __ init __ с другой подписью.
  2. Я не хочу вручную писать __ init __, который генерируется автоматически, чтобы добавить необязательный параметр
  3. Я не хочу добавлять необязательный параметр, чтобы принять dict, просто чтобы __ init __ оставался автоматически сгенерированным.

Я подумал о добавлении второго метода init2 , который вернет экземпляр класса данных и проанализирует переданный параметр dict в автоматически сгенерированном методе __ init __.


Буду признателен за ваш вклад, чтобы решить, является ли предложенное ниже решение правильной реализацией.

Кроме того, может ли эта реализация рассматриваться как тип фабрики?

Спасибо.


Продолжение: поскольку JSON \ словарь, полученный из запроса ES, выглядит следующим образом:

  1. Имеет те же ключевые слова, что и класс данных

  2. Плоский, т.е. вложенных объектов нет.

Я мог бы просто передать значения как ** dict в автоматически сгенерированный метод __ init __.

Смотрите мой ответ ниже для этого конкретного случая:


from dataclasses import dataclass

@dataclass
class MyData:
    name: str
    age: int = 17

    @classmethod
    def init_from_dict(cls, values_in_dict: dict):
        # Original line using MyData was fixed to use cls, following @ForceBru 's comment
        # return MyData(values_in_dict['name'], age=values_in_dict['age'])
        return cls(values_in_dict['name'], age=values_in_dict['age'])

my_data_1: MyData = MyData('Alice')
print(my_data_1)

my_data_2: MyData = MyData('Bob', 15)
print(my_data_2)

values_in_dict_3: dict = {
    'name': 'Carol',
    'age': 20
}

my_data_3: MyData = MyData.init_from_dict(values_in_dict_3)
print(my_data_3)

# Another init which uses the auto-generated __init__ works in this specific
# case because the values' dict is flat and the keywords are the same as the
# parameter names in the dataclass.
# This allows me to do this
my_data_4: MyData = MyData(**values_in_dict_3)
2
RaamEE 2 Июл 2019 в 12:29

3 ответа

Лучший ответ

В вашем коде есть потенциальная ошибка. Учти это:

class Thing:
    def __init__(self, a, b):
        self.a, self.b = a, b

    @classmethod
    def from_int(cls, value):
        return Thing(value, value + 1)

class AnotherOne(Thing):
    def __init__(self, a, b):
        self.a, self.b = a + 1, b + 2

Теперь, если вы запустите AnotherOne.from_int(6), вы получите Thing объект:

>>> AnotherOne.from_int(6)
<__main__.Thing object at 0x8f4a04c>

... хотя вы, вероятно, хотели создать объект AnotherOne!

Чтобы это исправить, создайте объект следующим образом:

class Thing:
    ...

    @classmethod
    def from_int(cls, value):
        return cls(value, value + 1)  # Use `cls` instead of `Thing`

Я думаю, что с вашим кодом все в порядке: действительно, один из способов использования classmethod предоставляет другие способы инициализации экземпляра класса, чем использование __init__.

3
ForceBru 2 Июл 2019 в 09:40

Как я писал в следующем разделе вопроса, в разделе _source ответа ElasticSearch используются те же ключевые слова, что и у параметров класса данных, и он плоский, что означает, что в JSON \ dict нет вложенных словарей.

Это позволяет мне реализовать следующее.

"_Source" моего ответа в упругом поиске выглядит так

response = {
  "_index": "env1",
  "_type": "_doc",
  "_id": "e3c85",
  "_score": 0.105360515,
  "_source": {
    "name": "RaamEEIL",
    "age": "19"
  }
}

Так что я мог бы просто сделать:

my_data = MyData(**response['_source'])

Это передает значения как пары ключ: значение в метод __ init __ и, поскольку имена совпадают, все работает гладко.

0
RaamEE 4 Июл 2019 в 10:31

Кроме того, может ли эта реализация рассматриваться как тип фабрики?

Да, это обычный шаблон для добавления from_<type> classmethod, поскольку python не поддерживает перегрузку методов.

1
abdusco 2 Июл 2019 в 09:41