Condition sur les imports / python

Résolu/Fermé
Telnaz - Modifié le 1 sept. 2022 à 10:49
mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 - 2 sept. 2022 à 15:05

Bonjour,

Je viens vers vous car j'ai du mal pour trouver la meilleure optimisation de mes algorithmes.

Je m'explique : j'ai un fichier nommé

gen.py

qui s'exécute en important des paramètres, des informations supplémentaires, comme par exemple des URL, des clés d'API, etc. Ces paramètres, sont situés dans un fichier appelé :

param.py

Cependant, j'ai plusieurs fichiers de paramètres, param_1.py, param_2.py, param_3.py, etc.

Donc, l'idée que j'ai eu est de passer en argument à gen.py des numéros tels que 1,2,3 etc. en fonction du fichier de paramètre que je veux prendre en compte. Ainsi, à l'intérieur du script gen.py, j'écris une condition sur cet argument, pour importer le bon fichier de paramètre.

Seulement, voici le début de mon script :

from param import url, key, ssl, outputdir, fls, vadl
try:
    from param import wdt
except ImportError:
    wdt = False

try:
    from param import lctgs
except ImportError:
    lctgs = True

try:
    from param import idl
except ImportError:
    idl = False

try:
    from param import eat
except ImportError:
    eat = []

J'ai plusieurs fois l'import de param, donc il faudrait que chaque if englobe tout ce code, par exemple mon if serait du type :

if (sys.argv ==1):
   from param_1 import ...

Cela ne me semble pas très optimisé, je voulais donc savoir s'il y avait une meilleure solution ?

Je ne l'ai pas précisé, mais ensuite il y aura Crontab qui lancera un script (script.sh) qui lui lancera les commandes :

python3 gen.py 1
python3 gen.py 2 
...

Avec vérification des erreurs.

J'ai donc aussi penser à faire sur ce script (script.sh) une modification de noms des fichiers param, par exemple exécuter un par un le cas 1,2,3 etc. et à chaque fois faire :

mv param_1.py param.py
python3 gen.py

...

Mais bon, ça ne me semble pas terrible

2 réponses

Whismeril Messages postés 19030 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 27 avril 2024 931
1 sept. 2022 à 10:49

Bonjour 

merci de forcer le langage python quand tu postes un code


0
mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 7 749
Modifié le 1 sept. 2022 à 14:48

Bonjour,

Il me paraît plus naturel d'avoir un seul fichier param.py qui expose une fonction load_params(i) et qui en fonction de i retourne le jeu de paramètres adéquat. Rien ne t'empêche ensuite de faire en sorte que load_param charge des informations depuis différents modules python en fonction de la valeur de i.

Ensuite, des paramètres sont comme leur nom l'indique... des paramètres. Donc quelque chose que l'utilisateur peut régler. Généralement, un utilisateur fournit les paramètres :

  • de manière interactive :
    • au travers d'une interface utilisateur (graphique ou texte), mettant en jeu éventuellement des fichiers tiers
  • au lancement du programme :
    • au travers d'un fichier de configuration (par exemple un fichier .ini ou .json)
    • au travers des paramètres passés au programme en ligne de commande, mettant en jeu éventuellement des fichiers tiers

Dans ton cas, j'ai l'impression que ton programme doit simplement prendre en paramètre un fichier de configuration dans lequel sont définis les paramètres (URL, clés d'API, etc). Tu peux par exemple t'inspirer de cet exemple.

Bonne chance

0

Bonjour et merci pour ta réponse.

De ce que j'ai compris, j'aurais donc un seul fichier param.py avec à l'intérieur une fonction load_param(i) qui serait appelée dans un for (par exemple) qui traitera toutes les valeurs de i possibles.

Donc ensuite, gen.py importe ce module param.py, qui renvoie les différentes en fonction de i c'est cela ?

Pour mieux comprendre, le fichier param.py contient :

url = 'https://IP/'

key = 'xxxx'

outputdir = 'output'

fs = {'pubd':'true'}

id = False

vadl = ['0', '1', '2', '3', '4', '5']

eat = []

wdt = False

wlt = True

Et à savoir que chaque valeur peut changer en fonction des "i".

En fait, il vaut mieux que je modifie mon script gen.py, que je lui fasse prendre les 9 paramètres en arguments et que ces paramètres soient donnés par le script param.py qui traite tous les cas possibles, non ? Est-ce optimisé ?

Ok donc j'ai lu la deuxième partie de ton message, cela revient à ce que j'ai dit juste avant, sauf que tu dis que je peux directement donner le fichier contenant les paramètres en argument c'est cela ?

0
mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 7 749 > Telnaz
Modifié le 1 sept. 2022 à 16:48

Oui c'est ça.

params.py

def load_params(i):
    if i == 0:
        return {
           "key" : "xxx",
           "output_dir" : "output",
           # ...
        }
    elif i == 1:
        return {
           "key" : "yyy",
           "output_dir" : "output2",
           # ...
        }
    #...
    else:
        raise RuntimeError(f"load_params: Invalid i parameter: {i}")

main.py

import sys
from optparse import OptionParser
    
def main():
    parser = OptionParser()
    parser.add_option(
        "-i", "--config-index",
        metavar = "INT",
        type    = "int",
        dest    = "config_index",
        help    = "Index of the configuration. 0 = ... ; 1 = ...",
        default = 0
    )
    (options, args) = parser.parse_args()
    print(f"options.config_index = {options.config_index}")
    params = load_params(options.config_index)
    print(params)
    # ...
    return 0
        
if __name__ == "__main__":
    sys.exit(main())

Exécution

(mando@silk) (~) $ python3 toto.py -i 1
options.config_index = 1
{'key': 'yyy', 'output_dir': 'output2'}

(mando@silk) (~) $ python3 toto.py --config-index 1
options.config_index = 1
{'key': 'yyy', 'output_dir': 'output2'}

(mando@silk) (~) $ python3 toto.py -i 0
options.config_index = 0
{'key': 'xxx', 'output_dir': 'output'}

(mando@silk) (~) $ python3 toto.py
options.config_index = 0
{'key': 'xxx', 'output_dir': 'output'}

Ici j'ai pris un entier mais tu peux imaginer travailler avec une chaîne, un booléan, bref ce qui est adapté à tes besoins (voir un tutoriel sur optparse). Ton programme peut bien évidemment avoir plusieurs options. On pourrait d'ailleur imaginer que tu aies un paramètre en ligne de commande pour chacun de tes paramètres (url de l'api, output_dir, etc...) + un paramètre qui les initialise toute en chargeant un fichier de configuration (ini ou json). On évite toutefois de mettre les clés en paramètres car sinon elles apparaissent en clair dans la table des processus.

Bonne chance

0
Telnaz > mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024
1 sept. 2022 à 18:45

Ok, merci beaucoup.

J'ai donc adapté le script gen.py (ou main.py), pour récupérer les différents paramètres et les mettre dans des variables globales, comme ceci :

url = ""
key = ""
output_dir = ""

# [...]

def main():

   # [...]

    global url
    url = params["url"]
    global key
    key = params["key"]
    global output_dir
    output_dir = params["output_dir"]

    return 0

# We launch the search for parameters
main()


# [...]

J'ai juste une petite question, la ligne

if __name__ == '__main__': 
      sys.exit(main())

signifie que la fonction main est exécuté seulement si le script exécuté en ligne de commande, et non importé c'est cela ?

0
mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 7 749 > Telnaz
Modifié le 2 sept. 2022 à 11:37
  • Concernant ton script, je te déconseille l'utilisation de variables globales. C'est généralement une mauvaise habitude car ça nuit à la lisibilité (on ne voit pas l'ensemble des paramètres d'une fonction) et ça peut avoir des effets de bords gênant (programmation parallèle). Mieux vaut les passer explicitement en paramètres.
  • Concernant les deux lignes en fin de fichier, oui cela sert quand on exécute le script python en ligne de commande.
  • Est-ce que ton problème est résolu ou as-tu d'autres questions ?
0
mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 7 749 > Telnaz
2 sept. 2022 à 15:05

Ces autres fonctions devraient simplement recevoir url, key etc en paramètre. Ça alourdit un peu la signature et l'appel des fonctions en questions, mais au moins on sait de quoi elles dépendent. Donc admettons que les fonctions en questions soient f(x, y) et g(x, y), que f a besoin de api et url, et que g à besoin de url et key, alors elles elles deviennent f(x, y, api, url) et g(x, y, url, key)

Ensuite, si ça a un sens de rassembler f, g, api, url et key, tu peux imaginer définir un objet qui aura api, url et key en attribut puis f et g en méthodes. En tant que méthodes, f et g auront accès à self.api, self.url, self.key.

class Gateway:
    def __init__(self, url, api, key):
        self.url = url
        self.api = api
        self.key = key
    def f(self, x, y):
        print(x, y, self.url, self.api)
    def f(self, x, y):
        print(x, y, self.api, self.key)

gateway = Gateway("url", "api", "key")
gateway.f(1, 2)
gateway.g(3, 4)

Ainsi la signature de f et g est plus légère.

1