Es ist eine Weile her, aber es ist eine Fortsetzung des Artikels, den ich zuvor geschrieben habe. Die, die ich dieses Mal gemacht habe, wurde auf github hochgeladen. Die, die mit pip installiert werden kann, ist für mich.
Fügen Sie den eigentlichen Code in [github] ein (https://github.com/akio-1978/object_converter).
Ich habe vorher etwas Ähnliches gemacht, aber der Code hat sich geändert und die Zeit ist vergangen, also werde ich von 1 schreiben, ohne zu brechen ..
Angenommen, Sie haben das folgende Diktat als Konvertierungsquelle. Es ist eine Struktur, die Diktat in Diktat enthält.
src_dict = {
'value' : 'AAA',
'nested' : {
'nested_value' : 'BBB'
}
}
Angenommen, Sie möchten dies in eine Klasse wie diese konvertieren. Ich möchte eine Instanz von NestedClass in "self.nested" von TastClass haben.
#Klasse 1
class TestClass():
def test_method(self):
return 'assigned value: ' + self.value
#Klasse 2-Bild hängt in Teil 1
class NestedTestClass:
def test_method(self):
return 'nested assigned value: ' + self.nested_value
Um die obige Konvertierung durchzuführen, denke ich, dass es notwendig ist, zuzuordnen, welches Element des Containers in welche Klasse konvertiert wird, also habe ich über diese Art von Diktat nachgedacht.
mapping = {
'<MAPPING_ROOT>' : TestClass, #Weil die Spitze keinen Namen hat'<MAPPING_ROOT>'Zu
'nested' : NestedClass
}
dict repräsentiert die Zuordnung von "[class / dict key]: [function]". Das Diktat selbst, auf das "src_dict" zeigt, hat keinen Namen. Verwenden Sie stattdessen "<MAPPING_ROOT>" als Schlüssel. Der Konstruktor wird als Funktion angegeben. Elemente, die nicht in "Zuordnung" enthalten sind, wie z. B. "src_dict [" Wert "]" in diesem Beispiel, werden so festgelegt, wie sie sich im Konvertierungsziel befinden.
Ich möchte es so benutzen.
usage
#Nehmen Sie eine Zuordnung im Konstruktor vor
converter = ObjectConverter(mapping=mapping)
#Übergeben Sie die Konvertierungsquelldaten an die Konvertierungsmethode
converted_class = converter.convert(src_dict)
#Rufen Sie eine Methode der konvertierten Klasse auf
converted_class.test_method()
Ich habe es so gemacht.
converter.py
class ObjectConverter:
#Mapping-Definition bei der Generierung erhalten
def __init__(self, *, mapping):
self.mapping = mapping
#Konvertierungsaufrufmethode
def convert(self, src):
#Das oberste Element ist die Zuordnung'<root>'Prämisse, die immer passt
return self._convert_value('<MAPPING_ROOT>', self.mapping['<MAPPING_ROOT>'], src)
#Bestimmen Sie die Verarbeitungsmethode anhand des Wertes
def _convert_value(self, key, func, value):
#Bei einer Liste werden alle Elemente mit func konvertiert
if isinstance(value, (list, tuple)):
return self._convert_sequence(key, func, value)
#Rufen Sie im Falle eines Diktats den Schlüssel und den Wert so ab, wie sie sind
if isinstance(value, dict):
return self._convert_dict(key, func, value)
#Für den Unterricht__dict__Und behandeln Sie es als Diktat
if isinstance(value, object) and hasattr(value, '__dict__'):
return self._convert_dict(key, func, value.__dict__)
#Wenn keiner der oben genannten Punkte zutrifft, geben Sie ihn unverändert zurück
return value
#Konvertieren Sie den Inhalt von dict
def _convert_dict(self, key, func, src):
# _call_Füllen Sie das von der Funktion erstellte Objekt
return self._assign_dict(self._call_function(key, func, src), key, src)
#Erstellung des durch Mapping angegebenen Objekts
def _call_function(self, key, func, src):
return func()
#Nehmen Sie den Inhalt des Diktats heraus und wenden Sie ihn an
def _assign_dict(self, dest, key, src):
for srcKey, value in src.items():
#Schlüssel wird im Mapping definiert
if srcKey in self.mapping:
func = self.mapping[srcKey]
#Führen Sie die zugeordnete Funktion aus und legen Sie das Ergebnis fest
self._set_value(dest, srcKey, self._convert_value(srcKey, func, value))
#Wenn sich der Schlüssel nicht in der Zuordnungsdefinition befindet, legen Sie ihn so fest, wie er ist
else:
#Ignorieren Sie alle Mapping-definierten Werte in dem hier übergebenen Wert
self._set_value(dest, srcKey, value)
#Der Status, in dem sich der Inhalt von src widerspiegelt
return dest
#Listenverarbeitung
def _convert_sequence(self, key, func, sequence):
current = []
for value in sequence:
current.append(self._convert_value(key, func, value))
return current
#Wertesetzer für Diktat und Klasse
def _set_value(self, dest, key, value):
if isinstance(dest, dict):
dest[key] = value
else:
setattr(dest, key, value)
#Dienstprogrammmethode zum Abrufen einer Instanz für die Diktatkonvertierung
#
@classmethod
def dict_converter(cls, mapping, *, dict_object=dict):
reverse_mapping = {}
#Machen Sie alle Mapping-Ziele diktieren
for key in mapping.keys():
reverse_mapping[key] = dict_object
#Instanz zum Diktieren konvertieren
return ObjectConverter(mapping=reverse_mapping)
** Die vollständigen technischen Daten finden Sie in der Quelle. ** Es passiert jedoch nichts. Es wird nur der Wert gescannt, der dem im Mapping enthaltenen Schlüssel entspricht. Grob gesagt mache ich das.
Wenn Sie "dict" extrahieren, wenn eine Klasse gefunden wird, müssen Sie nur das Diktat scannen. Ich möchte es nach Möglichkeit vermeiden, __dict__
zu berühren, aber dieses Mal werde ich den Aufwand vermeiden, andere Methoden zu verwenden. Wenn Sie es nicht mit einer speziellen Klasse zu tun haben, sollte es kein Problem geben. [^ berühre das nicht]
Der Wert, der nicht in der Zuordnung enthalten ist, wird unverändert auf das Konvertierungszielobjekt festgelegt. Selbst wenn der Wert zu diesem Zeitpunkt vorgegeben ist und ein Wert mit dem Namen in der Zuordnung enthalten ist, wird die Zuordnungsverarbeitung ausgeführt. Es wird nicht sein. Dies ist ein "verarbeitetes Diktat, das in der Zuordnung enthalten ist oder in der Zuordnung enthalten ist Ich mag den Fall der "Diktatverarbeitung" nicht und ich denke, dass die Datenstruktur, die eine solche Konvertierung erfordert, nicht sehr schön ist.
Für Fälle, in denen Sie eine Konvertierung von Klasse zu Diktat nach einer Konvertierung von Dikt zu Klasse durchführen möchten, um die ursprüngliche Form wiederherzustellen, haben wir eine Klassenmethode "dict_converter", um das Abrufen eines Rückwärtskonverters zu vereinfachen. Dies ist einfach, da alle Conversion-Ziele für die Zuordnung auf Dikt festgelegt sind. [^ nicht-diktieren-etwas]
test_objectconverter.py
import unittest
import json
from objectonverter import ObjectConverter
#Testklasse 1
class TestClass():
def test_method(self):
return 'TestObject.test_method'
#Testklasse Teil 2
class NestedTestClass:
def test_method(self):
return 'NestedObject.test_method'
class ClassConverterTest(unittest.TestCase):
#Legen Sie einfach die Eigenschaften der Stammklasse fest
def test_object_convert(self):
dict_data = {
'value1' : 'string value 1'
}
converter = ObjectConverter(mapping={'<MAPPING_ROOT>' : TestClass})
result = converter.convert(dict_data)
self.assertEqual(result.value1, 'string value 1')
#Versuchen Sie, die Methode der generierten Klasse aufzurufen
self.assertEqual(result.test_method(), 'TestObject.test_method')
#Generieren Sie eine verschachtelte Klasse
def test_nested_object(self):
#diktieren, dass json Schlüssel und Klassen zugeordnet werden
object_mapping = {
'<MAPPING_ROOT>' : TestClass,
'nested' : NestedTestClass
}
#Herkunftsquelle
dict_data = {
'value1' : 'string value 1',
'nested' : {
'value' : 'nested value 1'
}
}
converter = ObjectConverter(mapping=object_mapping)
result = converter.convert(dict_data)
self.assertEqual(result.value1, 'string value 1')
self.assertIsInstance(result.nested, NestedTestClass)
self.assertEqual(result.nested.value, 'nested value 1')
#Diktieren Sie einfach, wenn Sie keine Zuordnung angeben
def test_nested_dict(self):
object_mapping = {
'<MAPPING_ROOT>' : TestClass
}
#Herkunftsquelle
dict_data = {
'value1' : 'string value 1',
'nested' : {
'value' : 'nested value 1'
}
}
converter = ObjectConverter(mapping = object_mapping)
result = converter.convert(dict_data)
self.assertEqual(result.value1, 'string value 1')
self.assertIsInstance(result.nested, dict)
self.assertEqual(result.nested['value'], 'nested value 1')
#Listenverarbeitung
def test_sequence(self):
mapping = {
'<MAPPING_ROOT>' : TestClass,
'nestedObjects' : NestedTestClass,
}
source_dict = {
"value1" : "string value 1",
"nestedObjects" : [
{'value' : '0'},
{'value' : '1'},
{'value' : '2'},
]
}
converter = ObjectConverter(mapping=mapping)
result = converter.convert(source_dict)
self.assertEqual(result.value1, 'string value 1')
self.assertEqual(len(result.nestedObjects), 3)
for i in range(3):
self.assertIsInstance(result.nestedObjects[i], NestedTestClass)
self.assertEqual(result.nestedObjects[i].value, str(i))
#Wenn das Stammelement selbst eine Liste ist
def test_root_sequence(self):
object_mapping = {
'<MAPPING_ROOT>' : TestClass,
}
source_list = [
{'value' : '0'},
{'value' : '1'},
{'value' : '2'},
]
converter = ObjectConverter(mapping=object_mapping)
result = converter.convert(source_list)
self.assertIsInstance(result, list)
self.assertEqual(len(result), 3)
for i in range(3):
self.assertIsInstance(result[i], TestClass)
self.assertEqual(result[i].value, str(i))
# json -> class -> json
def test_json_to_class_to_json(self):
#Funktionen zur gegenseitigen Konvertierung von Klasse zu JSON
def default_method(item):
if isinstance(item, object) and hasattr(item, '__dict__'):
return item.__dict__
else:
raise TypeError
#diktieren, dass json Schlüssel und Klassen zugeordnet werden
object_mapping = {
'<MAPPING_ROOT>' : TestClass,
'nested' : NestedTestClass
}
#Herkunftsquelle-Zum besseren Vergleich in einer Zeile
string_data = '{"value1": "string value 1", "nested": {"value": "nested value 1"}}'
dict_data = json.loads(string_data)
converter = ObjectConverter(mapping=object_mapping)
result = converter.convert(dict_data)
dump_string = json.dumps(result, default=default_method)
self.assertEqual(dump_string, string_data)
#Das Ergebnis ist das gleiche, auch wenn es erneut konvertiert wird
result = converter.convert(json.loads(dump_string))
self.assertEqual(result.value1, 'string value 1')
self.assertIsInstance(result.nested, NestedTestClass)
self.assertEqual(result.nested.value, 'nested value 1')
#Umwandlung->Inverse Umwandlung
def test_reverse_convert(self):
dict_data = {
'value1' : 'string value 1'
}
mapping = {'<MAPPING_ROOT>' : TestClass}
converter = ObjectConverter(mapping=mapping)
result = converter.convert(dict_data)
self.assertEqual(result.value1, 'string value 1')
#Inversen Konvertierungskonverter generieren
reverse_converter = ObjectConverter.dict_converter(mapping=mapping)
reversed_result = reverse_converter.convert(result)
self.assertEqual(result.value1, reversed_result['value1'])
if __name__ == '__main__':
unittest.main()
Nun, ich frage mich, ob Sie die Grundlagen verstehen können Es gibt einen Testfall mit dem langen Namen "test_json_to_class_to_json", aber dies liegt daran, dass diese Klasse sich ursprünglich der Konvertierung in json sehr bewusst war.
Seit dem letzten Artikel ist mehr als ein halbes Jahr vergangen, aber tatsächlich habe ich endlich einen Job bekommen ... Ich hatte keine Zeit, also war es spät.
[^ generic-conteiners]: Da es lange dauert, "dict / list" viele Male unten zu schreiben, schreiben Sie es als Allzweckcontainer. Übrigens ist "Klasse" auch eine "Klasseninstanz", um genau zu sein, aber es ist eine "Klasse", weil sie lang ist.
[^ json-to-class]: Das erste, worüber ich nachdachte, war, wie ich mit json umgehen sollte, also wurde ich von json gefangen. Als ich den Dingen nachjagte, stellte ich fest, dass ich tatsächlich nach sehr einfachen Ergebnissen suchte. Es ist ein „Etwas“ -Fluss, aber Sie müssen genauer überlegen.
[^ dont-touch-this]: Ich mache es einfach, weil es einfach ist, aber __dict __
ist wie eine Hintertür, und wenn Sie den Wert von __dict __
abrufen, wird __getattribute__
möglicherweise nicht aufgerufen, sodass die Klasse nicht aufgerufen wird Es kann sich anders verhalten als ursprünglich beabsichtigt. Daran werde ich hier nicht denken.
[^ not-dict-Something]: Ich habe es geschrieben, um andere Typen als Ichiou-Diktat zu unterstützen, aber ich kann mir keine Verwendung dafür vorstellen.
Recommended Posts