Python 中定义数据类的正确方法是什么,该数据类既有自动生成的 __init__ 又有来自值字典的附加 init2
What is the proper way in Python to define a dataclass that has both an auto generated __init__ and an additional init2 from a dict of values
在 Python 中,我有一个包含十几个成员的数据类。我用它来创建一个我 post 进入 ElasticSearch.
的字典
现在我想从 ElasticSearch 得到一个字典 并用它来初始化数据类。
开始于:
- Python 不允许创建具有不同签名的第二个 __ init __。
- 我不想手动编写自动生成的 __ init __ 只是为了添加一个可选参数
- 我不想添加一个可选参数来接受字典,只是为了让 __ init __ 保持自动生成。
我想添加第二个方法 init2,它将 return 数据类的一个实例并将传递的 dict 参数解析为自动生成的 __ init __方法。
我会感谢您的意见,以确定我在下面建议的解决方案是否是正确的实施。
另外,这个实现可以看作是一种工厂吗?
谢谢。
Follow up: Since the JSON\dictionary I get from the ES request is:
Has exactly the same keywords as the dataclass
Is flat, i.d., there are no nested objects.
I could simply pass the values as a **dict into the the auto-generated __ init __ method.
See my answer below for this specific case:
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)
您的代码中可能存在错误。考虑一下:
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
的用法之一是提供其他方法来初始化 class 的实例,而不是使用 __init__
.
Also, Can this implementation be considered as a type of factory?
是的,添加 from_<type>
classmethod
是一种常见的模式,因为 python 不支持方法重载。
正如我在问题的后续部分中所写,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'])
这会将值作为 key:value 对传递给 __ init __ 方法,并且由于名称匹配,它可以顺利运行。
在 Python 中,我有一个包含十几个成员的数据类。我用它来创建一个我 post 进入 ElasticSearch.
的字典现在我想从 ElasticSearch 得到一个字典 并用它来初始化数据类。
开始于:
- Python 不允许创建具有不同签名的第二个 __ init __。
- 我不想手动编写自动生成的 __ init __ 只是为了添加一个可选参数
- 我不想添加一个可选参数来接受字典,只是为了让 __ init __ 保持自动生成。
我想添加第二个方法 init2,它将 return 数据类的一个实例并将传递的 dict 参数解析为自动生成的 __ init __方法。
我会感谢您的意见,以确定我在下面建议的解决方案是否是正确的实施。
另外,这个实现可以看作是一种工厂吗?
谢谢。
Follow up: Since the JSON\dictionary I get from the ES request is:
Has exactly the same keywords as the dataclass
Is flat, i.d., there are no nested objects.
I could simply pass the values as a **dict into the the auto-generated __ init __ method.
See my answer below for this specific case:
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)
您的代码中可能存在错误。考虑一下:
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
的用法之一是提供其他方法来初始化 class 的实例,而不是使用 __init__
.
Also, Can this implementation be considered as a type of factory?
是的,添加 from_<type>
classmethod
是一种常见的模式,因为 python 不支持方法重载。
正如我在问题的后续部分中所写,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'])
这会将值作为 key:value 对传递给 __ init __ 方法,并且由于名称匹配,它可以顺利运行。