IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Python Discussion :

objet "globalement" utilisable


Sujet :

Python

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Mars 2010
    Messages
    7
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2010
    Messages : 7
    Par défaut objet "globalement" utilisable
    Bonjour à tous,

    Je poursuis mon apprentissage de Python et de la programmation objet et plus ça va plus je trouve cela extrêmement intéressant et puissant.

    Par contre il semble que je ne maitrise pas bien les espaces de noms !

    Voila mon problème :

    Mon application est un ensemble de traitements de calculs statistiques, qui utilisent des objets communs.

    Pour cela j'ai donc créé :
    un module S3Library.py qui contient mes objets communs fonctionnels
    un module S3Tools.py qui contient des fonctions et des objets utilitaires

    Dans S3Tools, j'ai créé une classe S3Log, dont le but est d'ouvrir (__init__) un fichier texte en écriture, puis, au fur et à mesure du déroulement du calcul d'y ajouter des messages (méthode log) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class S3Log:
        """fichier log de traitement"""
        def __init__(self,fichier):
            self.logFile = open(fichier,'w')
     
        def log(self, niveau, message):
            log = str(datetime.now()) + " " + str(niveau) + " " + str(message) + "\n"
            self.logFile.write(log)
    Cela marche d'ailleurs bien, lorsque j'exécute le code ci-dessous, cela crée bien mon fichier Log.txt et écrit bien une première ligne dans celui-ci :

    $ cat Log.txt
    2010-05-13 11:49:24.128577 0 Debut de traitement
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # Codage :
    # -*- coding:Utf-8 -*- 
     
    # imports
    import os
    import csv
     
    from S3Library     import *
    from S3Tools        import *
     
    # Création d'une instance de fichier de log
     
    logFile = S3Log('Log.txt')
    logFile.log(0,"Debut de traitement")
    ensuite, ça ce corse :

    Pour chaque ligne lue dans le fichier EntrepotData.csv, je crée une instance de la classe Entrepot (module S3Library.py)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    # lecture des données et calcul
    Reader = csv.DictReader(open('EntrepotData.csv'), delimiter=';',quotechar='"')
    nomenclature = Reader.next()
     
    for row in Reader:
    # instanciation d'un entrepot
        entrepot = Entrepot(nomenclature, row)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    # -*- coding:Utf-8 -*- 
    #                                                                              #
    # Bibliothèque standard du moteur de calcul                                    #
    #                                                                              #
    # Cette bibliothèque contient l'ensemble des objets nécessaires au calcul      #
    #                                                                              #
    #______________________________________________________________________________#
     
    from UserDict   import UserDict
    from datetime   import datetime
     
    from S3Tools    import *
     
    # Classe Entrepot
    class Entrepot:
     
        def __init__(self, nomenclature, entrepot):
            for k, v in entrepot.items():
                w = typage(nomenclature, k, v)
                setattr(self, k, w)
    La fonction typage me permet de créer des attributs de l'instance Entrepot du bon type (chaine, entier, date, float,...) à partir des données de mon fichier csv qui sont toutes des strings. Cela me permet notamment de contrôler que les données reçues sont correctes (enfin au bon format..)

    Cette fonction devrait me renvoyer un message dans le fichier de log si un attribut ne correspond pas à son type :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    def typage(nomenclature, cle, valeur): 
        logAtt = None                                   
        try:
            typeValeur = nomenclature[cle]
        except:
            logAtt  = 1
            logMess = "Fonction typage : %s, donnée inexistante" % (cle)
        else:
            if typeValeur == "cr":
                return valeur
            elif typeValeur == "en":
                try:
                    return int(valeur)
                except:
                    logAtt  = 1
                    logMess = "Fonction typage: %s n'est pas un nombre entier valide (%s)" % (cle, valeur)
            elif typeValeur == "dc":
                try:
                    return float(valeur)
                except:
                    logAtt  = 1
                    logMess = "Fonction typage: %s n'est pas un nombre décimal valide (%s)" % (cle, valeur)
            elif typeValeur == "dt":
                try:
                    return datetime.strptime(valeur, "%d/%m/%Y")
                except:
                    logAtt  = 1
                    logMess = "Fonction typage: %s n'est pas une date valide (%s)" % (cle, valeur)
            elif typeValeur == "cr/en":
                try:
                    return int(valeur)
                except:
                    return valeur
            elif typeValeur == "cr/dc":
                try:
                    return float(valeur)
                except:
                    return valeur 
            elif typeValeur == "cr/dt":
                try:
                    return datetime.strptime(valeur, "%d/%m/%Y")
                except:
                    return valeur
     
            if logAtt:
                logFile.log(logAtt, logMess)
    Et là, j'ai systématiquement le message :

    logFile.log(logAtt, logMess)
    NameError: global name 'logFile' is not defined

    Je me doute qu'il s'agit d'un problème d'espace de nom, mais je sèche sur la solution. J'ai essayé de définir logFile comme global dans ma fonction typage, mais ça ne marche pas.

    Merci à l'avance de votre aide.

  2. #2
    Membre émérite
    Homme Profil pro
    Inscrit en
    Décembre 2007
    Messages
    758
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France

    Informations professionnelles :
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Décembre 2007
    Messages : 758
    Par défaut
    bonsoir,

    pourquoi ne pas simplement passer l'instance de S3Log en argument de la fonction ? ce qui implique de le donner aussi en paramètre à Entrepot.

  3. #3
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Mars 2010
    Messages
    7
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2010
    Messages : 7
    Par défaut
    Merci de l'aide Kango,

    Entre temps j'ai pensé à cette solution qui fonctionne, c'est vrai, passer logFile à Entrepot qui le repasse à la fonction typage. Néanmoins, cela veut dire qu'il faudra le faire pour toutes les fonctions qui appellent cette méthode et sur l'ensemble de l'applicatif, il risque d'y avoir beaucoup d'appels de ce genre, mais bon ...

    Au début j'avais utilisé une fonction et non un objet pour faire fonctionner mon fichier de log, et ça marchait très bien, si nécessaire je reviendrai plutôt à cette solution.

    En fait, je voudrais surtout comprendre pourquoi ça ne marche pas ?

    Là, l'usage de l'instance de S3Log fonctionne lorsque l'instance est créée ou la méthode log appelée depuis le programme principal, mais pas depuis une fonction d'un autre module, alors que l'instance existe.

    C'est ça que je ne comprends pas, pourquoi, puisque mon objet (instance) existe, je ne peux y faire référence ?

    mais je suis sûr qu'il y a une bonne raison, et j'avoue que les espaces de noms et les variables globales ou locales, j'ai pas bien saisi comment cela fonctionne, et j'ai beau lire des tutos ou des bouquins, ça ne s'éclaire pas vraiment..., ces notions sont un peu nouvelles pour moi et, il me semble assez différentes d'autres langage (procéduraux en tout cas).

  4. #4
    Membre Expert Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Par défaut
    Bonjour alphascorpii,

    Vu l'indentation de typage il semble que cette fonction soit dans S3Library.py et non dans la class Entrepot. En as tu besoin autre part ? Sinon pourquoi ne pas faire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    class Entrepot:
     
        def  __init__(self, nomenclature, entrepot):
            ....
     
        def typage(self, nomenclature, cle, valeur):
            ....
    A noter que si tu fais un from S3Tools import * dans S3Library il n'est pas besoin de la faire dans le principal.

    Sinon :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    logFile = S3Log('Log.txt')
    logFile.log(0,"Debut de traitement")
    Est dans le main ?

    @+

    Edit : Sinon teste tes variables avec un print globals()

  5. #5
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Mars 2010
    Messages
    7
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2010
    Messages : 7
    Par défaut
    Hello,

    Effectivement j'ai mal décrit l'organisation de mes modules. Comme je l'ai dit mon appli fait des calculs statistiques différents sur des objets communs.

    Donc là j'ai :

    Calc1.py : programme principal (c'est là que je crée mon objet logFile et édite le premier message de log, et que je crée mes instances d'Entrepot). l'objectif est de créer de nouveaux programmes (Calc2, 3,...) au fur et à mesure des besoins.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    # Codage :
    # -*- coding:Utf-8 -*- 
    #
    # Calc1
    #______
    # imports
    import os
    import csv
     
    from S3Library     import *
     
    # Création d'une instance de fichier de log
     
    logFile = S3Log('Log.txt')
    logFile.log(0,"Debut de traitement")
     
    # lecture des données et calcul
    Reader = csv.DictReader(open('EntrepotData.csv'), delimiter=';',quotechar='"')
     
    # défini la nomenclature du fichier
    nomenclature = Reader.next()
     
    for row in Reader:
    # instanciation d'un entrepot
        entrepot1 = Entrepot(nomenclature, row)
    #
    # ...suite du calcul
    S3Library.py : Classes "fonctionnelles", c'est à dire les objets communs qui seront utilisés par mes différents programmes de calcul, et qui contient notamment la classe Entrepot

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    # -*- coding:Utf-8 -*-
    #
    # S3Library.py 
    #                                                                              #
    # Bibliothèque standard du moteur de calcul                                    #
    #                                                                              #
    # Cette bibliothèque contient l'ensemble des objets nécessaires aux calculs    #
    #                                                                              #
    #______________________________________________________________________________#
     
    from UserDict   import UserDict
    from datetime   import datetime
    from S3Tools    import * 
     
    # Classe Entrepot
    class Entrepot:
     
        def __init__(self, nomenclature, entrepot):
            for k, v in entrepot.items():
                w = typage(nomenclature, k, v)
                setattr(self, k, w)
    S3Tools.py : objets et fonctions "utilitaires", communs à l'ensemble de l'application, pour l'instant la classe S3Log et la fonction typage

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    # -*- coding:Utf-8 -*-
    #
    # S3Tools.py
    #
    # outils du moteur de calcul
    #
    #   Fonction typage : typage et contrôle des attributs
    #_______________________________________________________________________________
     
    from datetime   import datetime
     
    #_______________________________________________________________________________
    # Fonction typage                                                              
    #
    # Défini le type et contrôle la validité des données lors de l'instanciation
    # des principaux objets du moteur de calcul
    #
    # en entrée :
    #   nomenclature : dictionnaire attribut/type
    #   cle : attribut à typer
    #   valeur : valeur de l'attribut
    #
    # en sortie :
    #   valeur : attribut de type recherché ou None
    #_______________________________________________________________________________
     
    def typage(nomenclature, cle, valeur): 
        logAtt = None                                   
        try:
            typeValeur = nomenclature[cle]
        except:
            logAtt  = 1
            logMess = "Fonction typage : %s, donnée inexistante" % (cle)
        else:
            if typeValeur == "cr":
                return valeur
            elif typeValeur == "en":
                try:
                    return int(valeur)
                except:
                    logAtt  = 1
                    logMess = "Fonction typage: %s n'est pas un nombre entier valide (%s)" % (cle, valeur)
            elif typeValeur == "dc":
                try:
                    return float(valeur)
                except:
                    logAtt  = 1
                    logMess = "Fonction typage: %s n'est pas un nombre décimal valide (%s)" % (cle, valeur)
            elif typeValeur == "dt":
                try:
                    return datetime.strptime(valeur, "%d/%m/%Y")
                except:
                    logAtt  = 1
                    logMess = "Fonction typage: %s n'est pas une date valide (%s)" % (cle, valeur)
            elif typeValeur == "cr/en":
                try:
                    return int(valeur)
                except:
                    return valeur
            elif typeValeur == "cr/dc":
                try:
                    return float(valeur)
                except:
                    return valeur 
            elif typeValeur == "cr/dt":
                try:
                    return datetime.strptime(valeur, "%d/%m/%Y")
                except:
                    return valeur
     
            if logAtt:
                S3Tools.logFile.log(logAtt, logMess)
     
    #_______________________________________________________________________________
    # Class S3Log                                                              
    #
    # Ecriture d'un message d'alerte dans le log du traitement
    #
    # en entrée :
    #   - niveau : niveau d'alerte 
    #     (0= information 1=warning, 2=erreur non fatale, 3=erreur fatale)
    #   - message à éditer
    #_______________________________________________________________________________
     
    class S3Log:
        """fichier log de traitement"""
        def __init__(self,fichier):
            self.logFile = open(fichier,'w')
     
        def log(self, niveau, message):
            log = str(datetime.now()) + " " + str(niveau) + " " + str(message) + "\n"
            self.logFile.write(log)
     
    #
    # EOF
    Mais en fait, je ne sais pas si c'est important, car j'ai fait un essai en regroupant S3Library et S3Tools dans le seul module S3Library, et mon problème subsiste : mon objet logFile instancié dans Calc1.py n'est visiblement pas utilisable dans ma fonction typage, ou en tout cas par une simple référence logFile.log.

    Dois-je le qualifier autrement ? genre quelquechose.logfile.log ?

    Je pensais naïvement que des variables ou des objets définis dans un niveau étaient accessibles aux niveaux inférieurs ?

  6. #6
    Membre Expert
    Homme Profil pro
    Inscrit en
    Mars 2007
    Messages
    941
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2007
    Messages : 941
    Par défaut
    Citation Envoyé par alphascorpii Voir le message
    Je pensais naïvement que des variables ou des objets définis dans un niveau étaient accessibles aux niveaux inférieurs ?
    Non en fait c'est plutôt le contraire, il faut importer un module pour avoir accès aux variables qui y sont déifinies. Il faudrait donc importer Calc1.py (ton module principal) dans S3Tools ce qui ne fonctionnera pas vu que cela créer des imports circulaires (Calc1 importe S3Library qui import S3Tools qui importe Calc1). De plus, c'est contraire au principe de modularité car tu veux pouvoir écrire d'autres programmes principaux.

    Ce qui t'a induit en erreur c'est la forme "from S3Tools import *". Cela importe (copie!) les noms définis dans S3Tools dans l'espace de nom du module courant mais les fonctions importées référencent toujours l'espace de nom du module, qui n'est pas accessible quand tu importes sous cette forme.

    Dans Calc1.py tu pourrais écrire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    ...
    import S3Tools
     
    S3Tools.logFile = S3Log('Log.txt')
    logFile = S3Tools.logFile  # uniquement si tu veux t'en servir dans Calc1 sans le préfixer à chaque fois
    Mais ce n'est pas très propre de créer ainsi des variables dans l'espace de nom d'un autre module.
    Tu peux aussi faire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    # dans Calc1.py:
    initLog('Log.txt')   # ou S3Tools.initLog('Log.txt')
    # ou logFile = initLog('Log.txt') si tu en as besoin dans le module principal
     
    # dans S3Tools.py:
    logFile = None
     
    def initLog(filename):
        global logFile
        logFile = S3Log(filename)
        return logFile
    De façon générale, il vaut mieux éviter la forme "from module import *" en faveur de "import module" ou "from module import nom1,nom2".

    Il ne faut pas avoir peur d'importer un module "un peu partout" pour y avoir accès. Si le module est déjà chargé, Python s'en rendra compte et rendra juste disponible l'espace de nom du module. Par exemple, on trouve parfois:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    import module
    from module import nom1, nom2
    Ce qui permet d'avoir une référence au module lui-même, et de copier nom1 et nom2 dans l'espace de nom courant pour pouvoir y accéder sans les préfixer.

    Il faut juste éviter les imports circulaires...

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo