Article
· Sept 2 6m de lecture

Introduction à la programmation Python dans le contexte d'IRIS

img

Cet article présente une introduction à la programmation Python dans le contexte d'IRIS.

Avant toute chose, je vais aborder un sujet important : Fonctionnement de Python. Cela vous aidera à comprendre certains problèmes et certaines limites que vous pourriez rencontrer lorsque vous utilisez Python dans IRIS.

Tous les articles et exemples sont disponibles dans ce dépôt git: iris-python-article

Fonctionnement de Python

Langage interprété

Python est un langage interprété, ce qui signifie que le code est exécuté ligne par ligne lors de l'exécution, même lorsque vous importez un script.

Qu'est-ce que cela veut dire? Examinons le code suivant:

# introduction.py

def my_function():
    print("Hello, World!")

my_function()

Lorsque vous exécutez ce script, l'interpréteur Python lit le code ligne par ligne. Il définit d'abord la fonction my_function, puis appelle cette fonction, qui affiche "Hello, World!" à la console.

Exemple d'exécution directe du script:

python3 /irisdev/app/src/python/article/introduction.py 

Cela donnera le résultat suivant:

Hello, World!

Dans un contexte IRIS, que se passera-t-il si nous importons ce script ?

Class Article.Introduction Extends %RegisteredObject
{
    ClassMethod Run()
    {
        Set sys = ##class(%SYS.Python).Import("sys")
        do sys.path.append("/irisdev/app/src/python/article")

        do ##class(%SYS.Python).Import("introduction")
    }
}

Lançons-le:

iris session iris -U IRISAPP '##class(Article.Introduction).Run()'

Cela donnera le résultat suivant:

Hello, World!

En effet, l'interpréteur Python importe le code en l'interprétant, il définit d'abord la fonction, puis l'appelle, exactement comme si vous exécutiez le script directement mais vous ne l'exécutez pas, vous l'importez.

⚠️ Remarque importante : si vous importez le script sans appeler la fonction, rien ne se passera. La fonction est définie, mais elle ne s'exécutera pas jusqu'à ce que vous l'appeliez explicitement.

Compris? L'interpréteur Python exécute le code dans le fichier, et si vous n'appelez pas la fonction, elle ne s'exécutera pas.

Exemple d'importation sans appel:

# introduction1.py
def my_function():
    print("Hello, World!")

Lançons-le dans un interpréteur Python:

python3 /irisdev/app/src/python/article/introduction1.py 

Résultat:

# Aucun résultat, puisque la fonction est définie mais n'est pas appelée

Dans un contexte IRIS, si vous importez ce script:

Class Article.Introduction1 Extends %RegisteredObject
{
    ClassMethod Run()
    {
        Set sys = ##class(%SYS.Python).Import("sys")
        do sys.path.append("/irisdev/app/src/python/article")
        do ##class(%SYS.Python).Import("introduction1")
    }
}

Lançons-le:

iris session iris -U IRISAPP '##class(Article.Introduction1).Run()'

Vous ne verrez aucun résultat,puisque la fonction est définie mais n'est pas appelée.

🤯 Pourquoi cette nuance est-elle importante?

  • Lorsque vous importez un script Python, celui-ci exécute le code contenu dedans.
    • Vous ne souhaitez peut-être pas que cela se produise
  • Vous pouvez être dérouté en pensant que l'importation d'un script revient à l'exécuter, mais ce n'est pas le cas.

Mise en cache des importations

Lorsque vous importez un script Python, l'interpréteur Python met en cache le script importé.
Cela signifie que si vous importez à nouveau le même script, il ne réexécutera pas le code de ce script, mais utilisera la version mise en cache.

Exemple:

Réutilisons le script introduction.py:

# introduction.py
def my_function():
    print("Hello, World!")

my_function()

Maintenant, faisons la même chose en réutilisant la classe Article.Introduction:

Class Article.Introduction Extends %RegisteredObject
{
    ClassMethod Run()
    {
        Set sys = ##class(%SYS.Python).Import("sys")
        do sys.path.append("/irisdev/app/src/python/article")
        do ##class(%SYS.Python).Import("introduction")
    }
}

Mais maintenant, nous allons l'exécuter deux fois de suite dans la même session IRIS:

iris session iris -U IRISAPP 

IRISAPP>do ##class(Article.Introduction).Run()
Hello, World!

IRISAPP>do ##class(Article.Introduction).Run()

IRISAPP>

🤯 Mais qu'est-ce que c'est que ça ?

Oui, Hello, World! n'est imprimé qu'une seule fois !

⚠️ Votre script importé est mis en cache. Cela signifie que si vous modifiez le script après l'avoir importé, les modifications ne seront pas prises en compte tant que vous n'aurez pas modifié la session IRIS.

Cela vaut également si vous utilisez la balise de langage python language tag dans IRIS:

Class Article.Introduction2 Extends %RegisteredObject
{

ClassMethod Run() [ Language = python ]
{
    import os

    if not hasattr(os, 'foo'):
        os.foo = "bar"
    else:
        print("os.foo already exists:", os.foo)
}

}

Lançons-le:

iris session iris -U IRISAPP

IRISAPP>do ##class(Article.Introduction2).Run()

IRISAPP>do ##class(Article.Introduction2).Run()
os.foo already exists: bar

Oh non, le module os est mis en cache et l'attribut foo n'est pas redéfini comme inexistant.

Conclusion

J'espère que cette introduction sera utile pour comprendre pourquoi, lorsque vous travaillez avec Python dans IRIS, vous pouvez rencontrer des comportements inattendus, notamment lors de l'importation de scripts et de la mise en cache.

À retenir lorsque vous travaillez avec Python dans IRIS:

  • Changez à chaque fois de session IRIS pour voir les modifications apportées à vos scripts Python.
    • Il ne s'agit pas d'un bug, c'est le fonctionnement normal de Python.
  • N'oubliez pas que l'importation d'un script exécute son code.

Bonus

Attendez! C'est illogique, si vous dites que lorsque vous importez un script, il est mis en cache. Pourquoi, lorsque je travaille avec la balise language tag = python, lorsque je modifie le script, cela fonctionne sans changer la session IRIS?

Bonne question, cela est dû au fait que la balise language tag est conçue pour que, chaque fois que vous l'exécutez, elle relise le script et l'exécute ligne par ligne comme s'il s'agissait de nouvelles lignes dans un interpréteur Python natif. La balise language tag n'importe pas le script, elle l'exécute simplement comme si vous l'exécutiez directement dans un interpréteur Python sans le relancer.

Exemple:

Class Article.Introduction2 Extends %RegisteredObject
{
ClassMethod Run() [ Language = python ]
{
    import os

    if not hasattr(os, 'foo'):
        os.foo = "bar"
    else:
        print("os.foo already exists:", os.foo)
}
}

Lançons-le:

iris session iris -U IRISAPP
IRISAPP>do ##class(Article.Introduction2).Run()

IRISAPP>do ##class(Article.Introduction2).Run()
os.foo already exists: bar  

Dans un interpréteur Python, cela se présentera comme suit:

import os

if not hasattr(os, 'foo'):
    os.foo = "bar"
else:
    print("os.foo already exists:", os.foo)

import os
if not hasattr(os, 'foo'):
    os.foo = "bar"
else:
    print("os.foo already exists:", os.foo)

Résultat:

os.foo already exists: bar # only printed once

Maintenant, c'est clair?

Suite:

  • Pep8
  • Modules
  • Méthodes Dunder
  • Utilisation de Python dans IRIS
  • ...
Discussion (0)1
Connectez-vous ou inscrivez-vous pour continuer