Versuchen Sie, Python-Code zu schreiben, um Go-Code zu generieren. - Versuchen Sie, JSON-to-Go usw. zu portieren

Versuchen Sie, Python-Code zu schreiben, um Go-Code zu generieren. - Versuchen Sie, JSON-to-Go usw. zu portieren

Dieser Artikel ist der 7. Tagesartikel von WACUL Adventskalender 2016. Ich arbeite seit September dieses Jahres bei WACUL und schreibe hauptsächlich Go-Code und manchmal Python im Backend.

Übrigens wurde es länger, während ich dies und das schrieb. Wenn Sie also nur wissen möchten, was Sie getan haben Einführung Nach dem Lesen von% 81% 98% E3% 82% 81% E3% 81% AB) [Endlich das Hauptthema](http://qiita.com/podhmo/items/a12952b07648e42911b4#%E3%82%88%E3 Bitte überspringen Sie zu ungefähr% 81% 86% E3% 82% 84% E3% 81% 8F% E6% 9C% AC% E9% A1% 8C).

Einführung

Vor kurzem schreibe ich jeden Tag Go-Code in meinem Geschäft. Gelegentlich wollte ich Go-Code in Python schreiben. Du denkst vielleicht, dass es seltsam ist. Aber bitte warten Sie. Derzeit ist es das Ergebnis der Verfolgung des folgenden Gedankenflusses.

  1. Überprüfen Sie, ob Sie innerhalb der normalen Funktionalität der go-Sprache etwas tun können
  2. Prüfen Sie, ob Sie die etwas gefährlichen Funktionen der go-Sprache verwenden können
  3. Überprüfen Sie, ob Sie mit den verschiedenen Go-Tools etwas tun können
  4. Go-Code in einer anderen Sprache generieren (an die ich gewöhnt bin)

Natürlich ist es sicherlich besser, die obigen Schritte so weit wie möglich auszuführen.

Der Unterschied zwischen * 1. * und * 2. * ist subjektiv und möglicherweise schwer zu verstehen.

Was * 3. * betrifft, fühle ich mich glücklich. Wenn nicht, kann es gut sein, es mit go zu machen.

Trotzdem denke ich, dass es Zeiten gibt, in denen es sich um eine interessenorientierte Suche handelt oder wenn Sie ein wenig in einer vertrauten Sprache arbeiten. Es kann schneller sein, schnell etwas zu erstellen, das sich nach Ihrem persönlichen Geschmack verhält, als sich zu bemühen, etwas für allgemeine Zwecke zu schaffen. Persönlich war ich mit Python vertraut, also habe ich mich für Python entschieden.

Gewöhnliche Codegenerierung

Wenn Sie an die Go-Code-Generierung denken, denken Sie normalerweise an die folgenden zwei Dinge.

Diesmal ist es eine andere Geschichte.

Schreiben wir Python-Code, um Go-Code zu generieren

prestring?

Eigentlich habe ich meine eigene Bibliothek für Arbeiten wie die Codegenerierung verwendet. Es ist eine Bibliothek namens prestring. Ich glaube nicht, dass es jemand weiß, deshalb werde ich ein wenig über diese Bibliothek erklären.

Diese Bibliothek ist keine (transpile) Bibliothek, die Go-Code aus Python oder DSL generiert, sondern nur eine Bibliothek zum direkten Handschreiben des Codes einer bestimmten Sprache Y (hier go) in Python. Dies entspricht der Verwendung einer Vorlagen-Engine für die automatische Codegenerierung. Das Gefühl, die volle Sprachfunktion zu nutzen, kann unterschiedlich sein.

Als Feature habe ich den Bereich hervorgehoben, der das Verwalten von Einrückungen erleichtert. Die ursprüngliche Idee selbstD Sprache für den Sieg|Programmierung| POSTDErscheintinArtikelnetc.srcgenDie Bibliothek ist abgeleitet von.

Ein Objekt, das schließlich zu einer Zeichenfolge wird

Der Prestring wird von einer Klasse namens "Module" für jedes Modul bereitgestellt. Der Code wird generiert, wenn das Ergebnis der Ausführung verschiedener Operationen für das Objekt dieser Klasse ausgegeben wird. Übrigens, obwohl es der Name Prestring ist, erinnere ich mich, dass es den Zustand vor der Zeichenkette bedeutete.

m = Module()
m.stmt("hai")
print(m)  # => "hai\n"

Code, der Code generiert, der Hallo Welt ausgibt

Schreiben wir Hallo Welt als einfachen Code. Hallo Welt sieht so aus:

from prestring.go import Module

m = Module()
m.package("main")

with m.import_group() as im:
    im.import_("fmt")

with m.func("main"):
    m.stmt("fmt.Println(`hello world`)")

print(m)

Verwenden Sie als Verwendung die Syntax with, wenn Sie einen Einzug wünschen. Sie können es mit dem Gefühl schreiben, eine Methode mit einem Namen zu verwenden, der dem reservierten Wort jeder Sprache ähnelt. Sobald Sie sich daran gewöhnt haben, können Sie die Ausgabesprache so sehen, wie sie ist. Wahrscheinlich. sicherlich. vielleicht.

Tatsächlich gehen die obigen Code-Ausgaben wie folgt aus: Selbst wenn die Ausgabe etwas klobig ist, ist dies praktisch, da gofmt sie formatiert.

package main

import (
	"fmt"
)

func main() {
	fmt.Println(`hello world`)
}

Code, der Code generiert, der das direkte Produkt berechnet

Lassen Sie uns automatisch einen etwas komplizierteren Code generieren. Ich möchte einen Code schreiben, der das direkte Produkt einer Liste und einer Liste berechnet. Zum Beispiel ist das direkte Produkt der beiden Listen xs und ys wie folgt.

xs = [1, 2, 3]
ys = ["a", "b", "c"]
[(x, y) for x in xs for y in ys]
# => [(1, 'a'), (1, 'b'), (1, 'c'), (2, 'a'), (2, 'b'), (2, 'c'), (3, 'a'), (3, 'b'), (3, 'c')]

Betrachten Sie in ähnlicher Weise zwei Fälle, drei Fälle, ... N Fälle und so weiter. Normalerweise würden Sie Code beliebig oft mit rekursivem usw. schreiben. Dieses Mal möchte ich einen Code schreiben, der den Code für jeden Fall ausgibt.

Code zur Berechnung des direkten Produkts bei N = 2 Code zur Berechnung

Schreiben wir zunächst den Code, der den Go-Code für die beiden Fälle direkt generiert. Es wird wie folgt aussehen.

from prestring.go import GoModule  #Gleich wie Modul


# cross product
def cross2(m):
    with m.func("cross2", "vs0 []string", "vs1 []string", return_="[][]string"):
        m.stmt("var r [][]string")
        with m.for_("_,  v0 := range vs0"):
            with m.for_("_,  v1 := range vs1"):
                m.stmt("r = append(r, []string{v0, v1})")
        m.return_("r")
    return m

m = GoModule()
m.package("main")
print(cross2(m))

Wenn mit angehängt ist, können Sie es irgendwie lesen, wenn Sie bedenken, dass es sich um einen Einzug handelt. Das Ausgabeergebnis ist wie folgt.

func cross2(vs0 []string, vs1 []string) [][]string {
        var r [][]string
        for _, v0 := range vs0 {
                for _, v1 := range vs1 {
                        r = append(r, []string{v0, v1})
                }
        }
        return r
}

Code zur Berechnung des direkten Produkts bei N = 5 Code zur Berechnung

Betrachten Sie nun den Fall, in dem die Anzahl der übergebenen Listen 3, 4, ... und eine beliebige Anzahl der übergebenen Listen beträgt. Es kann etwas ärgerlich sein zu sehen, wie viele Schleifennester sich abhängig von der Anzahl der übergebenen Listen ändern. In einem solchen Fall können Sie es nicht direkt so schreiben, wie es ist. Wenn Sie es jedoch rekursiv schreiben, können Sie Schleifencode generieren, der jederzeit verschachtelt ist, während die Einrückungsstruktur beibehalten wird.

def crossN(m, n):
    def rec(m, i, value):
        if i >= n:
            m.stmt("r = append(r, []string{{{value}}})".format(value=", ".join(value)))
        else:
            v = "v{}".format(i)
            vs = "vs{}".format(i)
            with m.for_("_, {} := range {}".format(v, vs)):
                value.append(v)
                rec(m, i + 1, value)

    args = ["vs{} []string".format(i) for i in range(n)]
    with m.func("cross{}".format(n), *args, return_="[][]string"):
        m.stmt("var r [][]string")
        rec(m, 0, [])
        m.return_("r")
    return m

Es ist etwas schwer zu lesen. Wenn man bedenkt, dass das innere mit geschrieben wird, bevor das äußere mit beendet wird, und der Aufruf verschachtelt ist, sieht die Struktur wie folgt aus. Gewöhnen Sie sich also bitte daran.

Bühnenschleife mit v0
Bühnenschleife mit v1
Schleife mit v2 Bühne
...
Schleife mit vN-Stufe

Wenn Sie versuchen, das Ergebnis von "crossN (m, 5)" auszugeben, wenn N = 5 ist, ist es wie folgt.

package main

func cross5(vs0 []string, vs1 []string, vs2 []string, vs3 []string, vs4 []string) [][]string {
	var r [][]string
	for _, v0 := range vs0 {
		for _, v1 := range vs1 {
			for _, v2 := range vs2 {
				for _, v3 := range vs3 {
					for _, v4 := range vs4 {
						r = append(r, []string{v0, v1, v2, v3, v4})
					}
				}
			}
		}
	}
	return r
}

Submodul () für Modul ()

Es gab noch eine weitere Besonderheit. Es ist eine Funktion namens Submodul. Diese Funktion ist eine Funktion, mit der Sie einer bestimmten Position in einer bestimmten Ausgabe einen Marker hinzufügen und den Zeichenfolgenausdruck einbetten können, den Sie später an dieser Position einfügen möchten. Es sieht wie folgt aus.

m = Module()

m.stmt("begin foo")
sm = m.submodule()
m.stmt("end foo")

m.stmt("bar")

sm.sep()
sm.stmt("** yay! **")
sm.sep()

print(m)

Erstellen Sie ein Submodul namens sm an der Position, die von foo von m umgeben ist. Ich werde später einen Zeilenumbruch und einige Formulierungen einbetten. Das Ausgabeergebnis ist wie folgt.

begin foo

** yay! **

end foo
bar

Diese Funktion wird im Importteil verwendet. So können Sie den Import des Pakets schreiben, das Sie später benötigen.

from prestring.go import Module

m = Module()
m.package('main')
with m.import_group() as im:
    pass

with m.func('main'):
    im.import_('log')
    m.stmt('log.Println("hmm")')

print(m)

"Log" wird zum ersten Mal in die Hauptfunktion importiert. Wenn Sie das Ausgabeergebnis betrachten, wird es an der durch "import_group ()" angegebenen Position eingefügt.

package main

import (
	"log"
)

func main()  {
	log.Println("hmm")
}

Es ist jedoch eine gute Idee, "goimports" anstelle von "gofmt" zu verwenden, um den Importteil automatisch einzufügen, sodass er in go möglicherweise nicht häufig verwendet wird.

Code erzeugt Code-Missbrauch

Code generieren Jetzt, da ich die Möglichkeit habe, Code zu generieren, hatte ich manchmal das Gefühl, alles mit der Codegenerierung zu tun. Zusammenfassend kann ich es nicht sehr empfehlen. Es ist am besten, das zu tun, was Sie zur Laufzeit berechnen können (ohne Reflektion zu verwenden).

Zum Beispiel fizzbuzz geschrieben von * ernsthafter Person * Ich bin nicht so glücklich, das zu tun. Weil ich es kann Die Unterscheidung zwischen gut und schlecht basiert auf meiner eigenen Faulheit. Bitte urteilen.

Persönlich finde ich die folgenden Kriterien gut.

Das letzte ist eine leicht generische Version des Generationslückenmusters (das ich kürzlich gelernt habe).

Außerdem wird der Versuch, die fehlenden Sprachfunktionen auszugleichen, wahrscheinlich fehlschlagen. Am Beispiel von Generika ist es möglich, eine einphasige "TContainer" -ähnliche Definition zu generieren, die "Container " entspricht. Ich kann so etwas wie eine Funktion nicht definieren, die "Container " als Argument verwendet. Dies liegt daran, dass T, das in den ursprünglichen Generika als Typvariable T behandelt wird, gleichzeitig mit der Generierung verschwindet und sich nicht ausbreitet. Es erfordert viel schlammige Arbeit, um es ernsthaft zum Laufen zu bringen, und am Ende fühlt es sich so an, als wäre es die Mühe nicht wert.

Endlich das Hauptthema

Es war eine lange Zeit, aber es ist endlich das Hauptthema. Es ist ungefähr auf halbem Weg hier.

JSON-to-GO?

Es scheint, dass Qiita den folgenden Artikel hatte.

Ich habe den obigen Artikel gelesen und herausgefunden. Es scheint einen praktischen Dienst namens JSON-to-Go zu geben. Dies bedeutet, dass die entsprechende Go-Typ-Definition ausgegeben wird, wenn Sie den JSON der API-Antwort übergeben.

Sie können sehen, wie es aussieht, indem Sie auf den Beispiellink in der GitHub-API klicken.

json-to-go-screenshot

Übrigens scheint die in diesem Service verwendete Implementierung des Konvertierungsteils auf Github zu sein. Es war Code in js geschrieben.

1. JSON-to-GO nach Python portieren

Es ist gut, dies so zu verwenden, wie es ist. Es ist ein gutes Thema, also werde ich versuchen, es auf Python zu portieren. Es ist jedoch ein Versuch, die oben erwähnte Bibliothek namens Prestring zu verwenden, um etwas zu erstellen, das ungefähr die gleiche Ausgabe und nicht einen vollständigen Port erzeugt.

Der portierte Code sieht wie folgt aus.

Es sind ungefähr 120 Zeilen, also ist es nicht so harter Code. Der ursprüngliche js-Code ist mit etwas mehr als 230 Zeilen auch etwas mehr als der Python-Code, aber nicht so groß. Der Grund für den Unterschied in der Codegröße ist, dass die ursprüngliche js-Implementierung den Prozess der Konvertierung in einen Namen für unterwegs definiert. Das ist reduziert, also sind es ungefähr 120 Zeilen. (Übrigens schien der Prozess der Konvertierung in den Namen für go, der im ursprünglichen json-to-go verwendet wurde, praktisch zu sein. Es gibt eine Geschichte, in der dasselbe im Prestring aufgenommen wurde.)

Nun, ich würde gerne die Portierungsarbeit erledigen, aber wenn ich die folgenden Operationen ausführen kann, kann ich anscheinend von JSON in Go-Code konvertieren.

Schauen Sie sich für jeden den Originalcode von json-to-go.js an und schreiben Sie, was Sie interessant finden.

Umwandlung in Namen für unterwegs

Einige der besseren Namenskonvertierungen für go sind:

>>> from prestring.go import goname
>>> goname("foo_id")
'FooID'
>>> goname("404notfund")
'Num404Notfund'
>>> goname("1time")
'OneTime'
>>> goname("2times")
'TwoTimes'
>>> goname("no1")
'No1'

Konvertierungen wie das Entfernen von Unterstrichen oder das Hinzufügen eines speziellen Präfixes zu Namen, die mit einer Zahl beginnen. So etwas wie Camel Case.

Beispielsweise scheinen URL, API, ASCII, ... speziell für ein bestimmtes Akronym verarbeitet zu werden. Ich gebe mein Bestes. Eine Liste dieser speziell behandelten Akronyme aus dem Golint-Code entnommen.

Erraten Sie den Go-Typ anhand des Werts jedes Felds in JSON

Grundsätzlich scheint der Typ eins zu eins mit einer if-Anweisung aus dem Wert des Ergebnisses der Deserialisierung von JSON verzweigt zu sein.

Ich dachte, es wären die folgenden drei Dinge.

  1. Konvertieren Sie ".0" in ".1", bevor Sie die übergebene JSON-Zeichenfolge deserialisieren
  2. In Bezug auf int und float sind die Typen so angeordnet, dass der bessere Typ ausgewählt wird.
  3. Wenn für jedes Feld der Struktur die Häufigkeit des Auftretens des Felds nicht mit der Häufigkeit des Auftretens der übergeordneten Struktur selbst übereinstimmt, lassen Sie es weg

Go-Typ-Definition generieren

Dies kann so wie es ist ordentlich geschrieben werden. Ich meine, ich habe den ursprünglichen js-Code nicht ernsthaft gelesen. Es ist kurz, also werde ich es hier posten. Es wird durch das folgende Verfahren erzeugt.

  1. json load
  2. Generierung von Strukturinformationen Konvertiert das Ergebnis der Analyse von json in einen benutzerfreundlichen Zwischenzustand
  3. Codegenerierung

Laden Sie für die JSON-Last von "1" einfach den übergebenen JSON. Als Vorprozess werde ich einen Prozess einschließen, um das obige ".0" in ".1" zu konvertieren (wahrscheinlich nicht erforderlich in Python, aber es scheint nur für den Fall zu sein).

Es ist die Erzeugung von Strukturinformationen von 2.. struct info ist keine Übertreibung, es ist das folgende Wörterbuch. Es ist ein Bild, das den gesamten JSON einmal analysiert und die folgenden Informationen verwendet.

{"freq": 1, "type": "int", "children": {}, "jsonname": "num"}

freq ist die Häufigkeit des Auftretens, type ist der Typ in go, Kinder enthalten untergeordnete Elemente, wenn es sich um ein Wörterbuch (struct) handelt, und jsonname ist der ursprüngliche json-Name (der Schlüssel des Wörterbuchs ist der Typname von go).

Dies ist der Codegenerierungsteil von 3.. Dies ist der Teil, in dem ich so schön schreiben kann, wie es ist. Ist es nicht eine Codeebene, die so gelesen werden kann, wie sie ist?

def emit_code(sinfo, name, m):
    def _emit_code(sinfo, name, m, parent=None):
        if sinfo.get("type") == "struct":
            with m.block("{} struct".format(name)):
                for name, subinfo in sorted(sinfo["children"].items()):
                    _emit_code(subinfo, name, m, parent=sinfo)
        else:
            m.stmt('{} {}'.format(name, to_type_struct_info(sinfo)))

        # append tag
        if is_omitempty_struct_info(sinfo, parent):
            m.insert_after('  `json:"{},omitempty"`'.format(sinfo["jsonname"]))
        else:
            m.insert_after('  `json:"{}"`'.format(sinfo["jsonname"]))

    with m.type_(name, to_type_struct_info(sinfo)):
        for name, subinfo in sorted(sinfo["children"].items()):
            _emit_code(subinfo, name, m, parent=sinfo)
    return m

Deshalb habe ich einen JSON für den Strukturkonverter.

Unter Verwendung von portiertem Code [github API-Antwort](https: // github). Das Ergebnis der Konvertierung von com / podhmo / advent2016 / blob / master / json / github.json) ist so sieht es aus .gehen). Da es sich um eine lange Ausgabe handelt, habe ich einen separaten Link erstellt.

Der JSON-to-Go-Konverter, den ich bekommen habe, also ändern wir ihn ein wenig.

Danach werde ich das Ausgabeergebnis weiterhin mit der JSON-API-Antwort von github überprüfen, die auch in JSON-to-Go verwendet wurde.

2. Versuchen Sie, die Korrespondenztyp zu ändern (verwenden Sie beispielsweise strfmt.Uri).

Möglicherweise möchten Sie einem anderen Typ eine bestimmte Zeichenfolge zuweisen, z. B. eine URI oder eine E-Mail-Adresse. Ändern Sie beispielsweise die Verwendung von Uri von strfmt.

Versuchen Sie es mit strfmt.Uri, wenn ": //" enthalten ist. Ich habe auch versucht, Import hinzuzufügen, wenn strfmt.Uri verwendet wird. Ändern Sie einige Zeilen.

Wenn Sie sich ernsthaft mit verschiedenen Typen befassen möchten, werden Sie anscheinend die Korrespondenz in dem Teil schreiben, der die Art des Go errät.

Der folgende Ausgabeteil

package autogen

type AutoGenerated struct {
	CloneURL        string      `json:"clone_url"`
	CreatedAt       time.Time   `json:"created_at"`
...

Es wurde wie folgt.

package autogen

import (
	"github.com/go-openapi/strfmt"
	"time"
)

type AutoGenerated struct {
	CloneURL        strfmt.Uri  `json:"clone_url"`
	CreatedAt       time.Time   `json:"created_at"`
...

3. Versuchen Sie, mit einer flachen Struktur auszugeben

Von hier an beginnt das Zögern. Die Änderungen, die ich für möglich hielt, wenn ich mich ein wenig um ein ungezwungenes Gefühl kümmerte, verursachten verschiedene problematische Probleme.

[Verknüpfter Artikel](http://qiita.com/minagoro0522/items/dc524e38073ed8e3831b#%E8%A4%87%E9%9B%91%E3%81%AA%E6%A7%8B%E9%80% A0% E3% 81% AE-json-% E5% 87% A6% E7% 90% 86% E3% 81% A7% E7% 9B% B4% E9% 9D% A2% E3% 81% 99% E3% 82 % 8B% E5% 95% 8F% E9% A1% 8C) war ziemlich dissed, aber ich habe einen Codegenerator. Ich wollte Änderungen einbeziehen, die die Ausgabeergebnisse erheblich verändern würden. Versuchen Sie, die Struktur der Ausgabestruktur von einer verschachtelten in eine Struktur mit einer flachen Struktur zu ändern. Vorerst habe ich beschlossen, den damaligen Feldnamen als Namen der Struktur zu verwenden.

Änderungen sind ungefähr 10 Zeilen.

Die folgende Definition

type AutoGenerated struct {
	CloneURL        strfmt.Uri  `json:"clone_url"`
...
	Name            string      `json:"name"`
	OpenIssuesCount int         `json:"open_issues_count"`
	Organization    struct {
		AvatarURL         strfmt.Uri `json:"avatar_url"`
		EventsURL         strfmt.Uri `json:"events_url"`

Es hat sich wie folgt geändert.

type AutoGenerated struct {
	CloneURL         strfmt.Uri   `json:"clone_url"`
...
	Name             string       `json:"name"`
	OpenIssuesCount  int          `json:"open_issues_count"`
	Organization     Organization `json:"organization"`
...

type Organization struct {
	AvatarURL         strfmt.Uri `json:"avatar_url"`
	EventsURL         strfmt.Uri `json:"events_url"`

Wenn Sie sich das Ausgabeergebnis ansehen, sehen Sie natürlich die [vorherige Ausgabe](https: // github). Da die Struktur der Verschachtelungsbeziehung, die in com / podhmo / advent2016 / blob / master / dst / jsontogo / github2.go) erhalten wurde, verschwunden ist, ist die Eltern-Kind-Beziehung des Werts meines Erachtens schwer zu verstehen. Machen.

4. Geben Sie den Wert Eltern-Kind-Beziehung in einem Kommentar an

Da die Struktur der Eltern-Kind-Beziehung von Werten in der flachen Ausgabe schwer zu verstehen ist, habe ich beschlossen, am Anfang der Strukturdefinition einen Kommentar hinzuzufügen, damit die Struktur der verschachtelten Beziehung übersehen werden kann.

Die Änderung beträgt ca. 20 Zeilen. Die Änderung der ursprünglichen Ausgabefunktion emit_code () selbst beträgt ungefähr 2 Zeilen.

Die Kommentare, die am Anfang der Definition hinzugefügt wurden, lauten wie folgt. Jetzt können Sie die Verschachtelungsbeziehung sehen.

/* structure
AutoGenerated
	Organization
	Owner
	Parent
		Owner
		Permissions
	Permissions
	Source
		Owner
		Permissions
*/

5. Verhindern Sie Namenskonflikte

Ich habe es übrigens bemerkt, und ich bin sicher, wenn Sie ein guter Mensch sind, werden Sie es sofort bemerken. Es gibt ein Problem. Namen können Konflikte verursachen. In der Ausgabe der ursprünglich verschachtelten Struktur musste die Struktur nicht benannt werden, da es sich um eine unmittelbare Definition handelte. Da die verschachtelte Ausgabe flach ist, benötigt die Strukturdefinition jetzt einen Namen. Ich habe den Feldnamen direkt verwendet. In der folgenden JSON-Datei treten beispielsweise Konflikte mit den Namen auf.

{
  "title": "Erstes Tagebuch",
  "author": "foo",
  "content": {
    "abbrev": "Ich werde ab heute mit dem Bloggen beginnen....",
    "body": "Ich werde ab heute mit dem Bloggen beginnen. Dieser Artikel ist der X. Tag des Adventskalenders.... ....."
  },
  "ctime": "2000-01-01T00:00:00Z",
  "comments": [
    {
      "author": "anonymous",
      "content": {
        "body": "hmm"
      },
      "ctime": "2000-01-01T00:00:00Z"
    }
  ]
}

Der Inhalt des Artikelteils und der Inhalt des Kommentarteils kollidieren.

/* structure
Article
	Comments
		Content
	Content
*/

//Inhalt des Artikelteils
type Content struct {
	Abbrev string `json:"abbrev"`
	Body   string `json:"body"`
}

//Der Inhalt des Kommentarteils
type Content struct {
	Body string `json:"body"`
}

Im Moment spielt es keine Rolle, ob es klobig ist, also vermeiden Sie Namenskonflikte. Verwenden Sie ein Objekt namens prestring.NameStore. Dies ist ein wörterbuchähnliches Objekt, das, wenn Sie einen Namen in den Wert einfügen, einen Namen zurückgibt, der Konflikte bei doppelten Namen vermeidet (überschreiben Sie "NameStore.new_name ()", um einen Namen zu generieren Sie können die Regeln ändern.

>>> from prestring import NameStore
>>> ns = NameStore()
>>> ob1, ob2 = object(), object()
>>> ns[ob1] = "foo"
>>> ns[ob2] = "foo"
>>> ns[ob1]
'foo'
>>> ns[ob2]
'fooDup1'

Änderungen sind ungefähr 10 Zeilen. Im Text wird ein Wert generiert, der für die Form des Wörterbuchs für struct eindeutig zu sein scheint, und der Name wird mit diesem als Schlüssel verwaltet.

/* structure
Article
	Comments
		ContentDup1
	Content
*/

type Content struct {
	Abbrev string `json:"abbrev"`
	Body   string `json:"body"`
}

type ContentDup1 struct {
	Body string `json:"body"`
}

CotentDup1 ist kein guter Name. Die Kollision kann vorerst vermieden werden. Es ist großartig zu sagen, dass die Pakete getrennt sind, damit Strukturen mit demselben Namen zusammen verwendet werden können. Zum Beispiel wäre es praktisch, wenn es eine Funktion gäbe, die leicht einen Namespace einführen könnte, wie beispielsweise das Ruby-Modul. Es scheint keine Funktion zu geben, die für unterwegs verwendet werden kann, also werde ich sie verlassen.

6. Kombinieren Sie doppelte Definitionen zu einer

Nachdem der Namenskonflikt behoben wurde, sehen wir uns die Ausgabe noch einmal an, indem wir den JSON der Github-API übergeben.

/* structure
AutoGenerated
	Organization
	Owner
	Parent
		Owner
		Permissions
	Permissions
	Source
		Owner
		Permissions
*/

Ich habe das Gefühl, dass Eltern und Quelle die gleiche Form haben. Blick hinein Es schien dieselbe Typdefinition zu sein. Tatsächlich ist die Typdefinition von Berechtigungen schon oft vorgekommen. Das Auto Generated der obersten Ebene selbst fühlt sich wie eine Obermenge wie Source an, aber ich werde es vorerst verlassen. Ich habe das Gefühl, ich möchte doppelte Definitionen zu einer kombinieren. Machen wir das.

Der geänderte Teil besteht aus ungefähr 10 Zeilen. Da das Scannen des Kommentarteils, der die verschachtelte Struktur der Struktur ausgibt, die in der vorherigen Änderung ausgegeben wurde (4. Fügen Sie die Eltern-Kind-Beziehung des Werts als Kommentar hinzu), und das Scannen bei der Ausgabe der Strukturdefinition in erster Linie unterschiedlich sind Trennt die Funktionen.

Das Ausgabeergebnis sieht aus wie Link, und ich konnte die doppelte Definition entfernen. Aber. Es gibt ein Problem. Es wird im nächsten Schritt behoben.

7. Verwenden Sie einen besseren Namen für den Namen des Strukturtyps

Es gibt noch ein Problem. Sie können dies sehen, indem Sie sich die Teile Parent und Source ansehen. Es hat die folgende Struktur und Source und Parent haben die gleiche Form.

/* structure
AutoGenerated
	Parent
		Owner
		Permissions
	Source
		Owner
		Permissions
*/

Die Tatsache, dass Parent und Source dieselbe Form haben, bedeutet, dass der gleiche Typ verwendet wird. Der Name dieses Typs ist nicht gut. Wenn überhaupt, ist Source immer noch ein besserer Name, aber ein Typ namens Parent wurde definiert und verwendet. traurig.

type AutoGenerated struct {
...
	Parent           Parent       `json:"parent"`
...
	Source           Parent       `json:"source"`
...

}

In der ursprünglichen JSON-to-GO-Struktur handelt es sich um eine anonyme Struktur, die sofort definiert wird. Sie müssen sich also keine Gedanken darüber machen. Da Sie jedoch eine flache Struktur erstellen, müssen Sie ihr einen Namen geben und diesen Namen angeben. Möglicherweise verfügen Sie nicht über die richtigen Informationen, um dies zu tun.

Ich werde versuchen, ein wenig zu widerstehen. Es ist etwas schwierig, aber ich werde später entscheiden, welcher Typname generiert werden soll. Verwenden Sie in solchen Fällen prestring.PreString.

from prestring import PreString
from prestring.go import GoModule

m = GoModule()
p = PreString("")
m.stmt("foo")
m.stmt(p)
m.stmt("bar")

p.body.append("*inner*")

print(m)

Natürlich können Sie das zuvor eingeführte Submodul verwenden. PreString ist das grundlegendste Objekt. Wenn Sie also später nur eine Zeichenfolge festlegen möchten, sollten Sie diese verwenden.

foo
*inner*
bar

Die Änderung betrug ungefähr 10 Zeilen

Es ist etwas schwer zu verstehen, aber der Modellname wird am Ende festgelegt. Insbesondere wird in der folgenden Form entschieden.

#Übergeben Sie eine Worttabelle, die als Argument abgezogen wird
name_score_map={"parent": -1, '': -10}

#Bestimmen Sie den Modellnamen vor dem Senden. Neu hier berechnet_Verwenden Sie name als Typnamen
new_name = max(candidates, key=lambda k: name_score_map.get(k.lower(), 0))

Im Ausgabeergebnis wurde die Verwendung von Quelle anstelle von Übergeordnet geändert. Die Umsetzung hier kann nach Geschmack und individuellem Geschmack entschieden werden. Beispielsweise können Sie den konvertierten Namen möglicherweise direkt angeben. In diesem Fall kann es als Bedingung für die Eingrenzung zweckmäßig sein, zum Zeitpunkt der Suche so etwas wie einen Pfad zu übergeben, einschließlich des Typs der obersten Ebene, anstatt nur dem Argument einen Namen zu geben.

Jedenfalls konnte ich den Modellnamen zum Zeitpunkt der Ausgabe zu einem besseren Namen machen.

type AutoGenerated struct {
...
	Parent           Source       `json:"parent"`
...
	Source           Source       `json:"source"`
...
}

8. Fügen Sie am Anfang der Strukturdefinition einen Kommentar hinzu (golint-kompatibel).

Es gibt einige Tools, die Code generieren. Dies ist nur ein kleines Murren, aber der von mockgen in golang / mock generierte Code fügt am Anfang der Definition keinen Kommentar hinzu. Dies wird von golang / lint gescholten, was mich sehr beunruhigt.

Dies ist immer eine automatische Generierung. Wenn Sie jedoch dem von golang / mock generierten Code einen Kommentar hinzufügen und ihn entsprechend gestalten, verschwinden alle, wenn die ursprüngliche Schnittstellendefinition ersetzt und neu generiert wird. .. Da es nicht geholfen werden kann, müssen wir eine spezielle Verarbeitung durchführen, z. B. das Ausschließen vom Ziel von Golint. Ich bin müde.

Fügen wir übrigens das Ergebnis dieser Codegenerierung hinzu, da am Anfang der Definition kein Kommentar steht.

Der geänderte Teil ist eine Zeile. Ich muss prestring.LazyFormat verwenden, da ich prstring.PreString verwende. Fügen Sie einfach eine Zeile hinzu. Es ist kurz vor einem Verdauungsspiel.

9. Da es sich um eine große Sache handelt, fügen Sie den ursprünglichen JSON-Wert als Beispiel zum Tag hinzu

Fügen wir außerdem den ursprünglichen JSON-Wert als Beispiel in das Tag ein, um festzustellen, welcher Wert eingegeben wird.

Änderungen sind einige Zeilen. Ich werde es leid, also werde ich es beenden.

Es wird jetzt im folgenden Format ausgegeben.

// AutoGenerated : auto generated JSON container
type AutoGenerated struct {
	CloneURL         strfmt.Uri  `json:"clone_url" example:"https://github.com/octocat/Hello-World.git"`
	CreatedAt        time.Time   `json:"created_at" example:"2011-01-26T19:01:12Z"`
	DefaultBranch    string      `json:"default_branch" example:"master"`
	Description      string      `json:"description" example:"This your first repo!"`
...

Der endgültige Code lautet hier.

Nachfolgend sind nur der erste und der letzte Code sowie die Ausgabeergebnisse aufgeführt.

(Nebenwirkungen der Beseitigung doppelter Definitionen)

Es ist ein kleiner Umweg. Ich war eine Weile ratlos und entschied mich für eine Ausgabe im Flachformat. Dabei haben wir eine Funktion hinzugefügt, um doppelte Definitionen zu entfernen. Dieses Entfernen doppelter Definitionen hatte einen unerwarteten Nebeneffekt. Es ist wie ein kleines Extra, aber ich werde es vorstellen.

Schauen Sie sich beispielsweise den folgenden JSON an.

{
  "total": 100,
  "left": {
    "total": 75,
    "left": {
      "total": 25,
      "left": {
        "total": 20
      },
      "right": {
        "total": 5
      }
    },
    "right": {
      "total": 50,
      "left": {
        "total": 25
      },
      "right": {
        "total": 25
      }
    }
  },
  "right": {
    "total": 25,
    "left": {
      "total": 20,
      "left": {
        "total": 10
      },
      "right": {
        "total": 10
      }
    },
    "right": {
      "total": 5
    }
  }
}

Dies ist etwas, das ein schöner Zweiviertelbaum ist.

Die Ausgabe davon im ursprünglich verschachtelten Format ist wie folgt. Da der Typ generiert wurde, der direkt dem übergebenen JSON-Wert entspricht, gibt es nicht nur eine nutzlose Strukturdefinition, sondern wenn sich die Struktur auch nur geringfügig ändert, kann sie nicht analysiert werden. Schlecht.

type Tree struct {
	Left struct {
		Left struct {
			Left struct {
				Total int `json:"total"`
			} `json:"left"`
			Right struct {
				Total int `json:"total"`
			} `json:"right"`
			Total int `json:"total"`
		} `json:"left"`
		Right struct {
			Left struct {
				Total int `json:"total"`
			} `json:"left"`
			Right struct {
				Total int `json:"total"`
			} `json:"right"`
			Total int `json:"total"`
		} `json:"right"`
		Total int `json:"total"`
	} `json:"left"`
	Right struct {
		Left struct {
			Left struct {
				Total int `json:"total"`
			} `json:"left"`
			Right struct {
				Total int `json:"total"`
			} `json:"right"`
			Total int `json:"total"`
		} `json:"left"`
		Right struct {
			Total int `json:"total"`
		} `json:"right"`
		Total int `json:"total"`
	} `json:"right"`
	Total int `json:"total"`
}

Wenn dies jedoch in einem flachen Format ausgegeben wird, das doppelte Definitionen eliminiert, sieht es wie folgt aus (Kommentare, die die Struktur angeben, werden weggelassen, weil sie ärgerlich sind). Es ist nur eine rekursive Definition, also ist es natürlich. gut. (Wenn dies jedoch unverändert bleibt, wird der Nullwert nicht bestimmt und er wird unendlich wiederholt, sodass es sich um einen Fehler handelt. * Es ist nutzlos, es sei denn, es wird zu Tree. Ist es ärgerlich, Verweise auf alle Strukturen als Zeiger zu verwenden? Suche nach einem Referenzzyklus zur Bestimmung des Endes)

// Tree : auto generated JSON container
type Tree struct {
	Left  Tree `json:"left"`
	Right Tree `json:"right"`
	Total int  `json:"total" example:"100"`
}

schließlich

Es ist lange her, aber das war's.

In diesem Artikel habe ich den Python-Code geschrieben, der den Go-Code generiert. Insbesondere habe ich versucht, einen Prozess zu implementieren, der der Konvertierung von JSON-to-Go-JSON in go-Strukturdefinition in Python ähnelt. Danach überließ ich es der Stimmung der Zeit und änderte den Code und das Ausgabeergebnis, während ich über meine Gedanken murmelte.

Ein wenig von dem, was ich dabei dachte, war, dass die Neuerfindung der Räder (in diesem Fall die Neuimplementierung) nicht unerwartet schlecht sein könnte. Zunächst erhalten Sie eine Implementierung, die alles vollständig erfasst. Sie werden wissen, wo und welche Änderungen an dieser Implementierung vorgenommen werden müssen, und es scheint, als ob es Ihre ist, und Sie möchten alles optimieren. Von dort aus beginnt eine persönliche Erkundung. Wie wäre es, diesen Teil so zu ändern? Möglicherweise können Sie die Unterteilungen und Ideen der Person sehen, die die ursprüngliche Implementierung erstellt hat, während Sie kleine Änderungen vornehmen und den Unterschied zwischen dem Änderungsergebnis und dem ursprünglichen Ergebnis vergleichen. .. In diesem Beispiel ist es beispielsweise seltsam, verschachtelte Ausgaben auszuwählen.

Und diesmal wurde es automatisch generiert, ohne die Art des Go zu berücksichtigen. Das nächste Mal dachte ich, es wäre schön, wenn ich über die automatische Generierung unter Berücksichtigung der Art des Go schreiben könnte.

Übrigens ist nur für diesen Zweck keine erneute Implementierung mit Python erforderlich, und es gibt die folgenden Tools, um die Definition von go struct aus GO made JSON auszugeben.

Nachtrag:

Einige Leute erwähnten Gojson im diesjährigen Adventskalender.

Recommended Posts

Versuchen Sie, Python-Code zu schreiben, um Go-Code zu generieren. - Versuchen Sie, JSON-to-Go usw. zu portieren
Schreiben wir Python-Code, der Go-Code analysiert und Go-Code generiert
Python 3.6 unter Windows ... und zu Xamarin.
So schreiben Sie Code für den Zugriff auf Python dashDB auf Bluemix oder lokal
Erste Python ② Versuchen Sie, Code zu schreiben, während Sie die Funktionen von Python untersuchen
Portieren und Ändern des Doublet-Solvers von Python2 auf Python3.
Komprimieren Sie Python-Daten und schreiben Sie in SQLite
Verstehen Sie Python-Listen, Wörterbücher und so weiter.
Setzen Sie Cabocha 0.68 in Windows ein und versuchen Sie, die Abhängigkeit mit Python zu analysieren
[Python] Erstellen Sie einen Linebot, um den Namen und das Alter auf das Bild zu schreiben
Versuchen Sie, eine Python- und Anaconda-Umgebung auf einem Mac zu erstellen (mit pyenv, conda).
Ich möchte Python-Code auf VS-Code nach meinen Wünschen formatieren und überprüfen
Versuchen Sie, das Programm "FORTRAN77 Numerical Computing Programming" auf C und Python zu portieren (Teil 1).
3,14 π Tag, versuchen Sie also, in Python auszugeben
Versuchen Sie, das Programm "FORTRAN77 Numerical Computing Programming" auf C und Python zu portieren (Teil 3).
Versuchen Sie, Python-Dokumente automatisch mit Sphinx zu generieren
Probieren Sie CI den Push-Python-Code auf GitHub aus.
Versuchen Sie, das Programm "FORTRAN77 Numerical Computing Programming" auf C und Python zu portieren (Teil 2).
So schreiben Sie in Error Repoting in Python auf GAE
Liste des zu verschiebenden und zu merkenden Python-Codes
Versuchen Sie, MLB-Daten auf Mac und Python zu importieren
Führen Sie eine Twitter-Suche in Python durch und versuchen Sie, Sätze mit der Markov-Kette zu generieren.
Die Geschichte der Portierung von Code von C nach Go (und zur Sprachspezifikation)
Ich möchte in Python schreiben! (1) Überprüfung des Codeformats
Probieren Sie die DB-Operation mit Python aus und visualisieren Sie sie mit d3
Tipps und Vorsichtsmaßnahmen beim Portieren von MATLAB-Programmen nach Python
Es ist nicht einfach, Python zu schreiben, es ist einfach, numpy und scipy zu schreiben
Schreiben Sie Tests in Python, um die Abdeckung zu profilieren und zu überprüfen
Schreiben Sie Code in UnitTest, eine Python-Webanwendung
Wie man Python auf Android genießt !! Programmieren für unterwegs !!
Guter und schlechter Code zum Vergleich mit Minikarten
"Cython" -Tutorial, das Python explosiv macht: Wenn C ++ - Code von der Bibliothek abhängt. Schreiben Sie setup.py.
[Python / Ruby] Mit Code verstehen Wie man Daten aus dem Internet abruft und in CSV schreibt
Eine Geschichte über die Portierung des Codes "Versuchen Sie zu verstehen, wie Linux funktioniert" nach Rust
Installieren Sie pyenv auf MacBookAir und wechseln Sie Python zur Verwendung
Versuchen Sie, ein Unterfenster mit PyQt5 und Python zu öffnen
Installieren Sie Python und Visual Studio Code unter Windows 10 (Version April 2020)
[Python] Verwenden Sie diese Option, um WAV-Dateien zu lesen und zu schreiben. [WAVIO]
So schreiben Sie eine Meta-Klasse, die sowohl Python2 als auch Python3 unterstützt
Es ist Halloween, also werde ich versuchen, es mit Python zu verstecken
[ROS] So schreiben Sie Publisher und Subscriber auf einen Knoten
Versuchen Sie einfach, einen Webhook mit ngrok und Python zu erhalten
Gehen Sie zur Sprache, um Teil 8 zu sehen und sich daran zu erinnern. Rufen Sie die GO-Sprache von Python aus auf
PyArmor ~ Einfache Möglichkeit, Python-Quellcode zu verschlüsseln und bereitzustellen ~
Python auf Ruby und wütend Ruby auf Python
Versuchen Sie, Python selbst zu verstehen
Befehl zum Generieren von QR-Code
Aktualisieren Sie die Python, die Sie auf Ihrem Mac hatten, auf 3.7-> 3.8
Schreiben Sie mit Python in csv
Generieren Sie QR-Code in Python
Konvertieren Sie Python 3.x-Code in Python 2.x.
Führen Sie Jupyter mit der REST-API aus, um Python-Code zu extrahieren und zu speichern
So installieren Sie OpenCV in Cloud9 und führen es in Python aus
Offline in Echtzeit, wie man ein Implementierungsbeispiel für E11 Ruby und Python schreibt
Ein Spiel für ein Abenteuer im interaktiven Python-Modus
[PEP8] Übernehmen Sie den Python-Quellcode und schreiben Sie ihn ordentlich
IME On / Off wird in Zusammenarbeit mit Python und Arduino durch LED angezeigt
Versuchen Sie, eine Python-Umgebung mit Visual Studio Code & WSL zu erstellen
Installieren Sie OpenCV2.4 (+ Python) schnell unter OS X und probieren Sie das Beispiel aus