Cet article est l'article du 7ème jour du Calendrier de l'Avent WACUL 2016. Je travaille chez WACUL depuis septembre de cette année, écrivant principalement du code go et parfois python sur le backend.
Au fait, cela a rallongé pendant que j'écrivais ceci et cela, donc si vous voulez juste savoir ce que vous avez fait [Introduction](http://qiita.com/podhmo/items/a12952b07648e42911b4#%E3%81%AF%E3 Après avoir lu% 81% 98% E3% 82% 81% E3% 81% AB), [Enfin le sujet principal](http://qiita.com/podhmo/items/a12952b07648e42911b4#%E3%82%88%E3 Veuillez passer à environ% 81% 86% E3% 82% 84% E3% 81% 8F% E6% 9C% AC% E9% A1% 8C).
Récemment, j'écris du code go tous les jours dans mon entreprise. Parfois, je voulais écrire du code go en python. Vous pouvez penser que c'est étrange. Mais attendez s'il vous plait. Pour le moment, c'est le résultat de suivre le flux de pensée suivant.
Bien sûr, il est certainement préférable de suivre les étapes ci-dessus autant que possible.
La différence entre * 1. * et * 2. * est subjective et peut être difficile à comprendre.
Quant à * 3. *, je me sens chanceux. Sinon, il peut être bon de le faire avec go.
Cela dit, je pense qu'il y a des moments où c'est une quête axée sur les intérêts, ou quand vous faites un peu de travail dans une langue familière. Il peut être plus rapide de créer rapidement quelque chose qui se comporte selon vos goûts personnels, plutôt que d'essayer de créer quelque chose à usage général. Personnellement, j'étais familier avec python, j'ai donc choisi python.
Lorsque vous pensez à la génération de code go, vous pensez généralement aux deux choses suivantes.
--Générer du code incorporé en utilisant text / template
etc.
--Générer du code en traçant AST et en le façonnant dans la forme requise
Cette fois, c'est une autre histoire.
prestring?
En fait, j'utilise ma propre bibliothèque pour des travaux tels que la génération de code. C'est une bibliothèque appelée prestring. Je ne pense pas que quiconque le sache, alors je vais vous expliquer un peu cette bibliothèque.
Cette bibliothèque n'est pas une bibliothèque (transpile) qui génère du code go à partir de python ou d'un DSL, mais en réalité juste une bibliothèque pour écrire directement le code d'un langage spécifique Y (ici) sur python. C'est la même chose que d'utiliser un moteur de modèle pour la génération automatique de code. Le sentiment d'utiliser la fonction de langage complet peut être différent.
En tant que fonctionnalité, j'ai mis l'accent sur la zone qui facilite la gestion des retraits. L'idée originale elle-mêmeLangue D pour la victoire|la programmation| POSTDApparaîtdanslesarticles,etc.srcgenLa bibliothèque est dérivée de.
Le prestring est fourni par une classe appelée «Module» pour chaque module. Le code est généré lorsque le résultat de l'exécution de diverses opérations sur l'objet de cette classe est sorti. Au fait, bien que ce soit le nom de prestring, je me souviens que cela signifiait l'état avant la chaîne de caractères.
m = Module()
m.stmt("hai")
print(m) # => "hai\n"
Écrivons bonjour le monde comme un simple code. bonjour le monde ressemble à ceci:
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)
Pour l'utiliser, utilisez la syntaxe with lorsque vous souhaitez une indentation. Vous pouvez l'écrire avec le sentiment d'utiliser une méthode avec un nom similaire au mot réservé de chaque langue. Une fois que vous vous y serez habitué, vous pourrez voir la langue de sortie telle qu'elle est. Probablement. sûrement. peut être.
En fait, le code ci-dessus génère un code comme celui-ci: Même si la sortie est un peu maladroite, c'est pratique car gofmt
la formatera.
package main
import (
"fmt"
)
func main() {
fmt.Println(`hello world`)
}
Générons automatiquement un code un peu plus compliqué. Je voudrais écrire un code qui calcule le produit direct d'une liste et d'une liste. Par exemple, le produit direct des deux listes xs et ys est le suivant.
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')]
De même, considérons deux cas, trois cas, ... N cas, et ainsi de suite. Normalement, vous écririez du code un nombre illimité de fois en utilisant récursif, etc. Cette fois, je voudrais écrire un code qui génère le code pour chaque cas.
Tout d'abord, écrivons le code qui génère directement le code go pour les deux cas. Cela ressemblera à ce qui suit.
from prestring.go import GoModule #Identique au module
# 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))
Si avec est attaché, vous pouvez le lire d'une manière ou d'une autre si vous gardez à l'esprit qu'il s'agit d'un retrait. Le résultat de sortie est le suivant.
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
}
Considérons maintenant le cas où le nombre de listes passées est 3, 4, ... et n'importe quel nombre de listes passées. Il peut être un peu ennuyeux de voir combien de nids de boucles changent en fonction du nombre de listes passées. Dans un tel cas, vous ne pouvez pas l'écrire directement tel quel, mais si vous l'écrivez de manière récursive, vous pouvez générer du code en boucle imbriqué à tout moment tout en conservant la structure d'indentation.
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
C'est un peu difficile à lire. Étant donné que l'intérieur avec est écrit avant de quitter l'extérieur avec et que l'appel est imbriqué, la structure est la suivante, veuillez donc vous y habituer.
boucle de scène avec v0
boucle de scène avec v1
boucle avec scène v2
...
boucle avec étape vN
Si vous essayez de sortir le résultat de crossN (m, 5)
lorsque N = 5, ce sera comme suit.
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
}
Il y avait une autre particularité. C'est une fonction appelée sous-module. Cette fonction est une fonction que vous pouvez ajouter un marqueur à une position spécifique dans une certaine sortie et incorporer l'expression de chaîne de caractères que vous souhaitez insérer à cette position plus tard. Cela ressemble à ce qui suit.
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)
Créez un sous-module appelé sm à la position entourée par foo de m. J'intégrerai un saut de ligne et une formulation plus tard. Le résultat de sortie est le suivant.
begin foo
** yay! **
end foo
bar
Cette fonction est utilisée dans la pièce d'importation. Ainsi, vous pouvez écrire l'importation du package dont vous aurez besoin plus tard.
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)
"Journal" est importé pour la première fois dans la fonction principale. En regardant le résultat de sortie, il est inséré à la position spécifiée par ʻimport_group () `.
package main
import (
"log"
)
func main() {
log.Println("hmm")
}
Cependant, c'est une bonne idée d'utiliser goimports
au lieu de gofmt
pour insérer automatiquement la partie d'importation, de sorte qu'elle ne soit pas beaucoup utilisée en cours de route.
Générer du code Maintenant que j'ai la capacité de générer du code, j'ai parfois l'impression que j'allais tout faire avec la génération de code. En conclusion, je ne le recommande pas beaucoup. Il est préférable de faire ce que vous pouvez calculer au moment de l'exécution (ne pas utiliser la réflexion).
Par exemple, fizzbuzz écrit par * personne sérieuse * Je ne suis pas si content de le faire. Parce que je peux le faire La distinction entre le bien et le mal est basée sur ma propre paresse. Veuillez juger.
Personnellement, je pense que les critères suivants sont bons.
―― Avant de commencer la génération de code, demandez-vous si vous en avez vraiment besoin.
Le dernier est une version légèrement générique du modèle d'écart de génération (que j'ai appris récemment).
De plus, essayer de compenser les fonctionnalités de langue manquantes échouera probablement.
En prenant les génériques comme exemple, il est possible de générer une définition de type TContainer
monophasée correspondant àContainer <T>
. Je ne peux pas définir quelque chose comme une fonction qui prend Container <T>
comme argument.
En effet, T, qui est traitée comme une variable de type T dans les génériques d'origine, disparaît en même temps qu'elle est générée et ne se propage pas. Il faut beaucoup de travail boueux pour que cela fonctionne sérieusement, et à la fin, on a l'impression que cela n'en vaut pas la peine.
Cela faisait longtemps, mais c'est finalement le sujet principal. C'est à peu près à mi-chemin ici.
JSON-to-GO?
Il semble que Qiita ait eu l'article suivant.
J'ai lu l'article ci-dessus et je l'ai découvert. Il semble y avoir un service pratique appelé JSON-to-Go. Cela signifie que si vous transmettez le JSON de API Response, la définition de type go correspondante sera sortie.
Vous pouvez voir à quoi il ressemble en cliquant sur l'exemple de lien dans l'API GitHub.
Au fait, l 'implémentation de la partie Conversion utilisée dans ce service semble être sur Github. C'était du code écrit en js.
Il est bon de l'utiliser tel quel. C'est un bon sujet, donc je vais essayer de le porter sur python. Cependant, il s'agit d'une tentative d'utiliser la bibliothèque appelée prestring mentionnée ci-dessus pour créer quelque chose qui produit approximativement la même sortie plutôt qu'un port complet.
Le code porté ressemblera au lien ci-dessous.
C'est environ 120 lignes, donc ce n'est pas ce code dur. Le code js original est également d'environ 230 lignes, un peu plus que le code python, mais pas si gros. La raison de la différence de taille de code est que l'implémentation js d'origine définit le processus de conversion en un nom pour aller. C'est réduit, donc c'est environ 120 lignes. (À propos, le processus de conversion vers le nom de go utilisé dans le json-to-go original semblait pratique. Il y a une histoire d'incorporation de la même chose dans la pré-chaîne)
Eh bien, j'aimerais réellement faire le travail de portage, mais si je peux faire les opérations suivantes, il semble que je puisse convertir de JSON en code.
Pour chacun, jetez un œil au code json-to-go.js original et écrivez ce que vous trouvez intéressant.
Certaines des meilleures conversions de nom pour go sont:
>>> from prestring.go import goname
>>> goname("foo_id")
'FooID'
>>> goname("404notfund")
'Num404Notfund'
>>> goname("1time")
'OneTime'
>>> goname("2times")
'TwoTimes'
>>> goname("no1")
'No1'
Conversions telles que la suppression des traits de soulignement ou l'ajout d'un préfixe spécial aux noms commençant par un nombre. Quelque chose comme Camel Case.
Par exemple, URL, API, ASCII, ... semble être spécialement traité pour un acronyme spécifique. Je fais de mon mieux. Pour une liste de ces acronymes spécialement traités tirés du code golint.
Fondamentalement, on a l'impression que le type est ramifié un à un avec une instruction if de la valeur du résultat de la désérialisation de JSON.
Je pensais que c'était les trois choses suivantes.
Cela peut être écrit proprement tel quel. Je veux dire, je n'ai pas lu sérieusement le code js original. C'est court donc je le posterai ici. Il est généré par la procédure suivante.
Pour la charge json de 1.
, chargez simplement le json passé. Comme pré-processus, je vais inclure un processus pour convertir le ".0" ci-dessus en ".1" (probablement pas nécessaire en python, mais cela semble être juste au cas où).
C'est la génération de struct info de «2.». struct info n'est pas une exagération, c'est le dictionnaire suivant. C'est une image qui analyse le JSON entier une fois et prend les informations suivantes.
{"freq": 1, "type": "int", "children": {}, "jsonname": "num"}
freq est la fréquence d'occurrence, type est le type dans go, children contient des éléments enfants s'il s'agit d'un dictionnaire (struct) et jsonname est le nom json d'origine (la clé du dictionnaire est le nom de type de go).
C'est la partie de génération de code de "3." C'est la partie où je peux écrire magnifiquement tel quel. N'est-ce pas un niveau de code qui peut être lu tel quel?
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
C'est pourquoi j'ai un convertisseur JSON to go struct.
En fait, en utilisant le code porté, la [réponse de l'API github](https: // github. Le résultat de la conversion de com / podhmo / advent2016 / blob / master / json / github.json) est voici à quoi il ressemble .aller). Puisqu'il s'agit d'une sortie longue, j'en ai fait un lien séparé.
Le convertisseur JSON-to-go que j'ai obtenu, modifions-le un peu.
Après cela, je continuerai à vérifier le résultat de sortie en utilisant le JSON de la réponse API de github, qui a également été utilisé dans JSON-to-Go.
Vous souhaiterez peut-être attribuer une chaîne spécifique, telle qu'un URI ou une adresse e-mail, à un type différent. Par exemple, changez pour utiliser Uri de strfmt.
Essayez d'utiliser strfmt.Uri si ": //" est inclus. J'ai également essayé d'ajouter l'importation lorsque strfmt.Uri est utilisé. Modifiez quelques lignes.
Si vous voulez traiter sérieusement différents types, il semble que vous écrirez la correspondance dans la partie qui devine le type d'aller.
La partie de sortie suivante
package autogen
type AutoGenerated struct {
CloneURL string `json:"clone_url"`
CreatedAt time.Time `json:"created_at"`
...
C'est devenu comme suit.
package autogen
import (
"github.com/go-openapi/strfmt"
"time"
)
type AutoGenerated struct {
CloneURL strfmt.Uri `json:"clone_url"`
CreatedAt time.Time `json:"created_at"`
...
A partir de là, c'est le début de l'hésitation. Les changements que je pensais possibles si je prenais un peu de soin avec un sentiment désinvolte ont commencé à causer divers problèmes gênants.
[Article lié](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) était plutôt dissed, mais j'ai eu un générateur de code. Je voulais incorporer des changements qui changeraient considérablement les résultats de sortie. Essayez de changer la structure de la structure de sortie d'une structure imbriquée à une structure plate. Pour le moment, j'ai décidé d'utiliser le nom du champ à ce moment-là comme nom de la structure.
Les changements concernent environ 10 lignes.
La définition suivante
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"`
Il a changé comme suit.
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"`
Certes, si vous regardez le Résultat de sortie, vous pouvez voir le [Sortie précédente](https: // github. Puisque la structure de la relation d'imbrication obtenue dans com / podhmo / advent2016 / blob / master / dst / jsontogo / github2.go) a disparu, j'ai l'impression que la relation parent-enfant de la valeur est difficile à comprendre. Faire.
Étant donné que la structure de la relation parent-enfant des valeurs est difficile à comprendre en sortie plate, j'ai décidé d'ajouter un commentaire au début de la définition de la structure afin que la structure de la relation imbriquée puisse être négligée.
Le changement est d'environ 20 lignes. Le changement de ʻemit_code () `lui-même de la fonction de sortie d'origine est d'environ 2 lignes.
Les commentaires qui ont été ajoutés au début de la définition sont les suivants. Vous pouvez maintenant voir la relation d'imbrication.
/* structure
AutoGenerated
Organization
Owner
Parent
Owner
Permissions
Permissions
Source
Owner
Permissions
*/
Au fait, je l'ai remarqué, et je suis sûr que si vous êtes une bonne personne, vous le remarquerez tout de suite. Il ya un problème. Les noms peuvent entrer en conflit. Dans la sortie de la structure imbriquée d'origine, la structure n'avait pas besoin d'être nommée car il s'agissait d'une définition immédiate. En raison de la mise à plat de la sortie imbriquée, la définition de structure a maintenant besoin d'un nom. J'utilisais directement le nom du champ. Par exemple, dans le JSON suivant, les noms seront en conflit.
{
"title": "Premier journal",
"author": "foo",
"content": {
"abbrev": "Je vais commencer à bloguer à partir d'aujourd'hui....",
"body": "Je vais commencer à bloguer à partir d'aujourd'hui. Cet article est le Xème jour du calendrier de l'Avent.... ....."
},
"ctime": "2000-01-01T00:00:00Z",
"comments": [
{
"author": "anonymous",
"content": {
"body": "hmm"
},
"ctime": "2000-01-01T00:00:00Z"
}
]
}
Le contenu de la partie article et le contenu de la partie commentaire entrent en conflit.
/* structure
Article
Comments
Content
Content
*/
//Contenu de la partie article
type Content struct {
Abbrev string `json:"abbrev"`
Body string `json:"body"`
}
//Le contenu de la partie commentaire
type Content struct {
Body string `json:"body"`
}
Pour le moment, peu importe si c'est maladroit, alors évitez les conflits de noms. Utilisez un objet appelé prestring.NameStore.
Il s'agit d'un objet de type dictionnaire qui si vous mettez un nom dans la valeur, il retournera un nom qui évite les conflits pour les noms en double (remplacez NameStore.new_name ()
pour générer un nom Vous pouvez changer les règles).
>>> from prestring import NameStore
>>> ns = NameStore()
>>> ob1, ob2 = object(), object()
>>> ns[ob1] = "foo"
>>> ns[ob2] = "foo"
>>> ns[ob1]
'foo'
>>> ns[ob2]
'fooDup1'
Les changements concernent environ 10 lignes. Une valeur qui semble être unique pour la forme du dictionnaire pour struct est générée dans le texte, et le nom est géré en utilisant cela comme clé.
/* structure
Article
Comments
ContentDup1
Content
*/
type Content struct {
Abbrev string `json:"abbrev"`
Body string `json:"body"`
}
type ContentDup1 struct {
Body string `json:"body"`
}
CotentDup1 n'est pas un bon nom. Pour le moment, la collision peut être évitée. C'est génial de dire que les packages sont séparés pour que les structures du même nom puissent être utilisées ensemble. Par exemple, ce serait pratique s'il y avait une fonction telle que le module ruby qui pourrait facilement introduire un espace de noms. Il ne semble y avoir aucune fonction qui puisse être utilisée pour aller, donc je vais la laisser.
Maintenant que le conflit de nom a été résolu, regardons à nouveau la sortie en passant le JSON de l'API Github.
/* structure
AutoGenerated
Organization
Owner
Parent
Owner
Permissions
Permissions
Source
Owner
Permissions
*/
Je sens que Parent et Source ont la même forme. Peeking inside Cela semblait être la même définition de type. Et, en fait, la définition de type des autorisations est apparue à plusieurs reprises. Le niveau supérieur Auto Generated lui-même ressemble à un sur-ensemble tel que Source, mais je le laisserai pour le moment. J'ai l'impression de vouloir combiner des définitions en double en une seule. Faisons le.
La partie modifiée fait environ 10 lignes. Depuis l'analyse de la partie commentaire qui sort la structure imbriquée de la structure qui a été sortie dans la modification précédente (4. Ajoutez la relation parent-enfant de la valeur en tant que commentaire) et l'analyse lors de la sortie de la définition de structure sont différentes, en premier lieu Séparé les fonctions.
Le résultat de sortie ressemble à lien, et j'ai pu supprimer la définition en double. Mais. Il y a un problème. Il sera corrigé à l'étape suivante.
Il reste un problème. Vous pouvez le voir en regardant les parties Parent et Source. Il a la structure suivante, et Source et Parent ont la même forme.
/* structure
AutoGenerated
Parent
Owner
Permissions
Source
Owner
Permissions
*/
Le fait que Parent et Source aient la même forme signifie que le même type est utilisé. Le nom de ce type n'est pas bon. Si quoi que ce soit, Source est toujours un meilleur nom, mais un type nommé Parent a été défini et utilisé. triste.
type AutoGenerated struct {
...
Parent Parent `json:"parent"`
...
Source Parent `json:"source"`
...
}
Dans le JSON-to-GO d'origine, il s'agit d'une structure anonyme qui est définie immédiatement, vous n'avez donc pas à vous en soucier, mais en faisant une structure plate, vous devez lui donner un nom et également spécifier ce nom Vous ne disposez peut-être pas des informations appropriées pour le faire.
J'essaierai de résister un peu. C'est un peu délicat, mais je déciderai plus tard du nom de type à générer. Dans de tels cas, utilisez 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)
Bien sûr, vous pouvez utiliser le sous-module présenté précédemment. PreString est l'objet le plus basique, donc si vous souhaitez simplement définir une chaîne plus tard, vous devez l'utiliser.
foo
*inner*
bar
Le changement était d'environ 10 lignes
C'est un peu difficile à comprendre, mais le nom du modèle est décidé à la fin. Plus précisément, il est décidé sous la forme suivante.
#Passer une table de mots qui seront déduits comme argument
name_score_map={"parent": -1, '': -10}
#Déterminez le nom du modèle avant d'émettre. Nouveau calculé ici_Utiliser le nom comme nom du type
new_name = max(candidates, key=lambda k: name_score_map.get(k.lower(), 0))
Changé pour utiliser Source au lieu de Parent dans le résultat de sortie. La mise en œuvre ici peut être décidée en fonction du goût et du goût individuel. Par exemple, vous pourrez peut-être spécifier directement le nom converti. Dans ce cas, comme condition de réduction, il peut être pratique de passer quelque chose comme chemin au moment de la recherche, y compris le type de niveau supérieur au lieu de simplement le nom de l'argument.
Quoi qu'il en soit, j'ai pu faire du nom du modèle au moment de la sortie un meilleur nom.
type AutoGenerated struct {
...
Parent Source `json:"parent"`
...
Source Source `json:"source"`
...
}
Il existe de nombreux outils qui génèrent du code. C'est juste un petit grognement, mais le code généré par mockgen
dans golang / mock n'ajoute pas de commentaire au début de la définition. Ceci est grondé par golang / lint, ce qui me rend très dérangé.
Et c'est toujours une génération automatique, mais si vous ajoutez un commentaire au code généré par golang / mock et que vous le faites correspondre, tout disparaîtra lorsque la définition d'interface originale sera remplacée et régénérée. .. Comme cela ne peut pas être aidé, nous devons faire un traitement spécial tel que l'exclure de la cible de golint. Je suis fatigué.
Au fait, ajoutons le résultat de cette génération de code car il n'y a pas de commentaire au début de la définition.
La partie modifiée est une ligne. J'ai besoin d'utiliser prestring.LazyFormat car j'utilise prstring.PreString. Ajoutez simplement une ligne. C'est proche d'un match de digestion.
De plus, incluons la valeur JSON d'origine comme exemple dans la balise pour savoir quelle valeur sera entrée.
Les changements se font sur quelques lignes. J'en ai marre, alors je vais le terminer.
Il est maintenant sorti dans le format suivant.
// 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!"`
...
Le code final est ici.
Seuls le premier et le dernier code et les résultats de sortie sont répertoriés ci-dessous.
C'est un petit détour. J'étais perdu pendant un moment et j'ai décidé de choisir une sortie au format plat. Dans le processus, nous avons ajouté une fonction pour supprimer les définitions en double. Cette suppression des définitions en double a eu un effet secondaire inattendu. C'est comme un petit plus, mais je vais vous le présenter.
Par exemple, jetez un œil au JSON suivant.
{
"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
}
}
}
C'est quelque chose qui est un bel arbre de deux quarts.
La sortie de ceci dans le format imbriqué d'origine est la suivante. Puisque le type qui correspond directement à la valeur JSON passée a été généré, non seulement il y a une définition de structure inutile, mais si la structure change même un peu, il ne sera pas possible d'analyser. mal.
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"`
}
D'un autre côté, si cela est sorti dans un format plat qui élimine les définitions en double, ce sera comme suit (les commentaires qui spécifient la structure sont omis car ils sont ennuyeux). C'est juste une définition récursive, donc c'est naturel. bien. (Cependant, si cela est laissé tel quel, la valeur zéro ne sera pas déterminée et elle se répétera indéfiniment, donc ce sera une erreur. * Il est inutile à moins que cela ne devienne Tree. Est-il ennuyeux d'utiliser des références à toutes les structures comme pointeurs? Sérieusement Recherche d'un cycle de référence pour déterminer la fin)
// Tree : auto generated JSON container
type Tree struct {
Left Tree `json:"left"`
Right Tree `json:"right"`
Total int `json:"total" example:"100"`
}
Ça fait longtemps, mais c'est tout.
Dans cet article, j'ai écrit le code python qui génère le code go. Plus précisément, j'ai essayé d'implémenter un processus similaire à la conversion de JSON-to-Go JSON pour aller à la définition de structure en python. Après cela, je l'ai laissé à l'ambiance de l'époque et j'ai changé le code et changé le résultat de sortie tout en marmonnant mes pensées.
Quand j'ai écrit un peu sur ce que je pensais dans le processus, c'était que la réinvention de la roue (réimplémentation dans ce cas) pourrait ne pas être étonnamment mauvaise. Tout d'abord, vous obtiendrez une implémentation qui a une compréhension complète de tout. Vous saurez où et quelles modifications apporter à cette implémentation, et vous aurez l'impression que c'est la vôtre, et vous voudrez tout modifier. De là, c'est le début d'une exploration personnelle. Que diriez-vous de changer cette partie comme ça? Vous pourrez peut-être voir les divisions et les idées faites par la personne qui a créé l'implémentation d'origine tout en apportant de petites modifications et en comparant la différence entre le résultat du changement et le résultat d'origine. .. Par exemple, dans cet exemple, il est étrange de sélectionner une sortie imbriquée.
Et cette fois, il a été généré automatiquement sans tenir compte du type de go. La prochaine fois, j'aimerais pouvoir écrire sur la génération automatique en tenant compte du type de go.
À propos, en ce qui concerne cette utilisation, il n'est pas nécessaire de le réimplémenter en python, et il existe les outils suivants pour générer la définition de go struct à partir de GO made JSON.
Post-scriptum:
Certaines personnes ont mentionné gojson dans le calendrier de l'Avent de cette année.
Recommended Posts