[Python] Konvertieren Sie Allzweckcontainer und Klasse ineinander

Grobe Zusammenfassung

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.

Was ich machen wollte

  1. Ich habe versucht, die von json empfangene Datenstruktur zu verarbeiten und mit einer anderen Datenstruktur in DynamoDB abzulegen.
  2. Müde von mehr Code, der direkt mit Diktat und Liste arbeitet. Ich fühle mich wie eine objektorientierte Person, die aussagekräftigen Daten selbst Verhalten verleihen möchte.
  3. Konvertieren Sie es dann in eine Klasse. Lassen Sie uns die Verarbeitungsergebnisklasse in dikt konvertieren.

Was ich tatsächlich getan habe

  1. Konvertieren Sie "Struktur aus Allzweckcontainern wie dict und list" in "beliebige Klassenstruktur". Und umgekehrt. [^ generic-conteiners]
  2. Ich habe ein Mapping mit dict erstellt, um die Konvertierungsregel zu definieren, und eine Klasse erstellt, um danach zu konvertieren.
  3. Im Unit-Test wird ein Anwendungsbeispiel vorgestellt.

Fügen Sie den eigentlichen Code in [github] ein (https://github.com/akio-1978/object_converter).

Mach es sofort

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 ..

Bild

Angenommen, Sie haben das folgende Diktat als Konvertierungsquelle. Es ist eine Struktur, die Diktat in Diktat enthält.

Quelle der Umwandlung

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.

Zielklasse

#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

Informationen, die zwei Daten verbinden

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.

Wie benutzt man

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()

Implementierung

Ich habe es so gemacht.

Ich habe es als converter.py gemacht, aber es ist lang. Falten Sie es nach oben.

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)

Grober Kommentar

** 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.

Berühren Sie den Inhalt der Klasse

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]

Fall, der nicht auf den Schlüssel reagiert

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.

Klasse, um die Konvertierung zu diktieren

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]

Unit-Test, der auch die Verwendung erklärt

Es ist lang, also falten Sie es.

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()

Kommentar

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.

Entschuldigung für die Schlange

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

[Python] Konvertieren Sie Allzweckcontainer und Klasse ineinander
[Python-Lernteil 3] Konvertieren Sie Pandas DataFrame, Series und Standard List ineinander
Versuchen Sie, Breiten- / Längen- und Weltkoordinaten mit Python ineinander umzuwandeln
[Python] Konvertieren Sie Dezimalzahlen in Binär-, Oktal- und Hexadezimalzahlen
Konvertieren / Zurückgeben von Klassenobjekten in das JSON-Format in Python
Python: Klassen- und Instanzvariablen
[Python] Datum in Zeichenfolge konvertieren
Konvertieren Sie numpy int64 in python int
[Python] Liste in Pandas konvertieren [Pandas]
Konvertieren Sie das Scratch-Projekt in Python
Python-Klassen- und Instanzvariablen
[Python] Konvertieren Sie Shift_JIS in UTF-8
Konvertieren Sie Python 3.x-Code in Python 2.x.
[Python] Wie man mit Klassenvariablen mit Dekorator und Metaklasse spielt
Wie man Youtube in MP3 konvertiert und es super sicher herunterlädt [Python]
Konvertieren Sie Videos mit ffmpeg + python + opencv in Schwarzweiß
Bestimmen Sie das Datums- und Uhrzeitformat mit Python und konvertieren Sie es in Unixtime
Python 3.6 unter Windows ... und zu Xamarin.
[Einführung in Python3 Tag 1] Programmierung und Python
Konvertieren Sie Markdown in Python in PDF
Python-Klassendefinitionen und Instanzbehandlung
Workflow zum Konvertieren der Formel (Bild) in Python
Konvertieren Sie die Liste mit Python in DataFrame
[Python] Weg zur Serpent (3) Python-Klasse
Lesen Sie die Big-Endian-Binärdatei in Python und konvertieren Sie sie in ndarray
Python> Liste> Doppelte Liste in einfache Liste konvertieren
[Python] Konvertiert natürliche Zahlen in Ordnungszahlen
Perl-Objekt und Python-Klasse Teil 1.
Python-Protokollierung und Dump an JSON
Konvertieren Sie die Dezimalzahl in n-ary [Python]
Konvertieren Sie das Ergebnis von Python Optparse, um es zu diktieren und zu verwenden
Selen und Python zum Öffnen von Google
Ich war süchtig nach falschen Klassenvariablen und Instanzvariablen in Python
Python> Tupel> Konvertiert doppeltes Tupel in einzelnes Tupel
Lesen Sie die CSV-Datei mit Python und konvertieren Sie sie unverändert in DataFrame
[Python] So konvertieren Sie eine Datenbankdatei in CSV
So verpacken und verteilen Sie Python-Skripte
Von Python bis zur Verwendung von MeCab (und CaboCha)
Konvertieren Sie Memos sofort mit Python 2to3
Konvertieren Sie Python> Zwei-Wert-Sequenz in Wörterbuch
So installieren und verwenden Sie pandas_datareader [Python]
[Python] Wie man eine Klasse iterierbar macht
Binden Sie Methoden an Python-Klassen und -Instanzen
[Python] So konvertieren Sie eine zweidimensionale Liste in eine eindimensionale Liste
So konvertieren Sie Python in eine exe-Datei
[Python] Konvertieren Sie CSV-Dateibegrenzer in Tabulatortrennzeichen
Binden Sie an die Klasse, um YAML zu lesen und zu schreiben
Konvertieren Sie die psd-Datei in Python in png
Konvertieren Sie Excel-Daten mit Python in JSON
Konvertiere Hiragana mit Python (Beta) in Romaji
Fraktal zum Erstellen und Spielen mit Python
Konvertieren Sie von Katakana zu Vokal Kana [Python]
Python> Tupel in Liste konvertieren> aList = Liste (pi_tuple)
[Python] Unterschied zwischen Klassenmethode und statischer Methode
Portieren und Ändern des Doublet-Solvers von Python2 auf Python3.
Lesen Sie Python csv und exportieren Sie es nach txt
Python: Verwendung von Einheimischen () und Globalen ()