En Python, j'ai une classe de données qui contient plus d'une douzaine de membres. Je l'utilise pour créer un dict que je publie dans ElasticSearch .
Maintenant, je veux obtenir un dict à partir d'ElasticSearch et l'utiliser pour initialiser la classe de données. P >
Depuis:
J'ai pensé à ajouter une deuxième méthode init2 , qui retournera une instance de la classe de données et analysera le paramètre dict passé dans la méthode __ init __ générée automatiquement. p >
J'apprécierais votre contribution pour décider si ma solution suggérée ci-dessous est la bonne implémentation.
De plus, cette implémentation peut-elle être considérée comme un type d'usine?
Merci.
Suivi: puisque le dictionnaire JSON \ que j'obtiens de la requête ES est:
A exactement les mêmes mots-clés que la classe de données
Est plat, i.d., il n'y a pas d'objets imbriqués.
Je pourrais simplement passer les valeurs sous forme de ** dict dans la méthode __ init __ générée automatiquement.
Voir ma réponse ci-dessous pour ce cas spécifique:
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)
3 Réponses :
Il y a un bogue potentiel dans votre code. Considérez ceci:
class Thing:
...
@classmethod
def from_int(cls, value):
return cls(value, value + 1) # Use `cls` instead of `Thing`
Maintenant, si vous exécutez AnotherOne.from_int (6) , vous obtiendrez un objet Thing : p >
>>> AnotherOne.from_int(6) <__main__.Thing object at 0x8f4a04c>
... alors que vous vouliez probablement créer un objet AnotherOne !
Pour résoudre ce problème, créez l'objet comme ceci:
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
Je pense que votre code est par ailleurs très bien: en effet, l'une des utilisations de classmethod est de fournir d'autres moyens d'initialiser une instance d'une classe que d'utiliser __init__.
De plus, cette implémentation peut-elle être considérée comme un type d'usine?
Oui, c'est un modèle courant pour ajouter des from_ classmethod car python ne prend pas en charge la surcharge de méthode.
Comme je l'ai écrit dans la section de suivi de la question, la section _source de la réponse ElasticSearch a les mêmes mots-clés que les paramètres de la classe de données et est plate, ce qui signifie qu'il n'y a pas de dictionnaires imbriqués dans le JSON \ dict.
Cela me permet de mettre en œuvre ce qui suit.
La "_source" de ma réponse dans la recherche élastique ressemble à ceci
my_data = MyData(**response['_source'])
Je pourrais donc simplement faire:
response = {
"_index": "env1",
"_type": "_doc",
"_id": "e3c85",
"_score": 0.105360515,
"_source": {
"name": "RaamEEIL",
"age": "19"
}
}