Article
· Août 3, 2022 5m de lecture

Sérialisation des objets Python dans des globales

Motivation

Ce projet a vu le jour lorsque j'ai réfléchi à la manière de permettre au code Python de traiter naturellement le mécanisme de stockage évolutif et de récupération efficace fourni par les globales IRIS, par le biais de la technologie Embedded Python.

Mon idée initiale était de créer une sorte d'implémentation de dictionnaire Python en utilisant les globales, mais j'ai vite réalisé que je devais d'abord m'occuper de l'abstraction des objets.

J'ai donc commencé à créer des classes Python capables d'envelopper des objets Python, de stocker et de récupérer leurs données dans des globales, c'est-à-dire de sérialiser et de désérialiser des objets Python dans des globales IRIS.

Comment cela fonctionne-t-il ?

Comme ObjectScript %DispatchGetProperty(), %DispatchSetProperty() et %DispatchMethod(), Python dispose de méthodes permettant de déléguer les appels aux propriétés et aux méthodes des objets.

Lorsque vous définissez ou récupérez une propriété d'objet, l'interpréteur Python vous permet d'intercepter cette opération par les méthodes __setattr__(self, name, value) et __getattr(self, name)__.

Regardez cet exemple de base :

>>> class Test:
...         def __init(self, prop1):
...                 self.prop1 = prop1
...         def __setattr__(self, name, value):
...                 print(f"setting property {name} to value {value}")
...         def __getattr__(self, name):
...                 print(f"getting property {name}")
...
>>> obj = Test()
>>> obj.prop1 = "test"
setting property prop1 to value test
>>> obj.prop1
getting property prop1
>>> obj.prop2
getting property prop2
>>> obj.prop2
getting property prop2
>>>

Notez que les méthodes __setattr__() et __getattr()__ ont été appelées indirectement par les opérations set et get sur les objets de la classe Test - qui implémente ces méthodes. Même une propriété non déclarée, comme prop2 dans cet exemple, émet des appels vers elles.

Ce mécanisme est le cœur du test de sérialisation que j'ai essayé dans mon projet python-globales-convertisseur-exemple. Avec ce mécanisme, vous pouvez intercepter les opérations set/get et stocker/récupérer les données des globales IRIS.

Modèle d'objet

Les globales offrent une structure hautement personnalisable. En utilisant leur modèle hiérarchique pour l'accès aux informations, qui est assez similaire aux objets JSON et aux dictionnaires Python, nous pouvons stocker les données des propriétés des objets et les métadonnées.

Voici comment j'ai utilisé une globale pour créer un modèle d'objet simple pour sérialiser des objets Python :

Pour chaque objet sérialisé, son nom de classe est sérialisé dans un nœud étiqueté "class" :

^test(1,"class")="<class 'employee.SalaryEmployee'>"

Le premier indice est un nombre incrémental qui est utilisé comme référence à cet objet dans le modèle. Ainsi, dans l'exemple ci-dessus, un objet de la classe employee.SalaryEmployee est stocké avec la valeur de référence 1.

Pour les propriétés de types de données primitives, leur type et leur valeur sont stockés. Par exemple :

^test(1,"name","type")="<class 'str'>"
^test(1,"name","value")="me"

Cette structure est interprétée comme l'objet référencé par l'index 1, a une propriété appelée name, avec une valeur égale à 'me'.

Pour les propriétés référençant des objets, le modèle est légèrement différent, car contrairement aux objets JSON ou aux dictionnaires Python, les globales sont destinés à stocker uniquement des données de type primitif. Donc un autre noeud "classe" est créé pour cet objet référencé, et son index de noeud (c'est-à-dire sa référence) est stocké dans le noeud de propriété :

^test(1,"company","oref")=2
^test(1,"company","type")="<class 'iris_global_object.IrisGlobalObject'>"
^test(2,"class")="<class 'employee.Company'>"
^test(2,"name","type")="<class 'str'>"
^test(2,"name","value")="Company ABC"

Ces structures signifient que l'objet 1 a une propriété appelée company, dont les valeurs sont stockées dans l'index 2 - notez la valeur de ^test(1, "company", "oref").

Processus de sérialisation/désérialisation

Lorsque vous créez un wrapper pour sérialiser ou désérialiser des objets Python, vous devez définir le nom de la globale qui stocke l'objet.

Ensuite, le processus de sérialisation est effectué lorsqu'une opération set est exécutée. La méthode __setattr__() définit la valeur et le type de la propriété dans la globale définie pour stocker l'objet, en utilisant le modèle d'objet simple expliqué précédemment.

Dans le sens inverse, une désérialisation est effectuée par la méthode __getattr__, lorsqu'une opération get est effectuée.

Pour les types de données primitifs, ce processus est simple : il suffit de récupérer la valeur stockée dans la globale et de la retourner.

Mais pour les objets, le processus doit instancier le type de données de leur classe et définir également toutes leurs propriétés. De cette façon, un objet Python restauré peut être utilisé, y compris les appels à ses méthodes.

Les travaux futurs

Comme nous l'avons dit au début de cette entrée, ce projet est né comme une simplification d'une façon de laisser le code Python utiliser les globales comme un moteur de stockage naturel, et vise à être juste une preuve de concept.

La sérialisation/désérialisation d'objets n'est que le début de cet objectif. Il y a donc beaucoup d'efforts à faire pour que cette idée arrive à maturité.

J'espère que cette entrée vous permettra de comprendre le but de mon travail dans ce projet, et qu'elle pourra vous inspirer à réfléchir à de nouvelles façons d'utiliser les globales IRIS pour rapprocher Python d'IRIS.

Discussion (0)2
Connectez-vous ou inscrivez-vous pour continuer