2

在 Python 中,我有一个包含十几个成员的数据类。我用它来创建一个我发布到 ElasticSearch的字典。

现在我想从 ElasticSearch中获取一个 dict并用它来初始化数据类。

自从:

  1. Python 不允许创建具有不同签名的第二个 __ init __。
  2. 我不想手动编写自动生成的 __ init __ 只是为了添加一个可选参数
  3. 我不想添加一个可选参数来接受字典,只是为了让 __ init __ 保持自动生成。

我想添加第二个方法init2,它将返回数据类的实例并将传递的 dict 参数解析为自动生成的 __ init __ 方法。


我会根据您的意见来决定我下面建议的解决方案是否是正确的实现。

另外,这个实现可以被认为是一种工厂吗?

谢谢。


跟进:由于我从 ES 请求中得到的 JSON\dictionary 是:

  1. 具有与数据类完全相同的关键字

  2. 是flat,id,没有嵌套对象。

我可以简单地将值作为 **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)
4

3 回答 3

4

您的代码中存在潜在的错误。考虑一下:

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是提供其他方法来初始化类的实例,而不是 using __init__

于 2019-07-02T09:40:08.313 回答
1

另外,这个实现可以被认为是一种工厂吗?

是的,添加from_<type> classmethods 是一种常见的模式,因为 python 不支持方法重载。

于 2019-07-02T09:41:09.457 回答
1

正如我在问题的后续部分中所写,ElasticSearch 响应的 _source 部分与数据类的参数具有相同的关键字并且是扁平的,这意味着 JSON\dict 中没有嵌套字典。

这使我能够实现以下内容。

我在弹性搜索中的响应“_source”看起来像这样

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

所以我可以简单地做:

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

这会将值作为键:值对传递给 __ init __ 方法,并且由于名称匹配,因此可以顺利运行。

于 2019-07-04T10:31:48.033 回答