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 :

surcharge de modules


Sujet :

Python

  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    Mars 2010
    Messages
    18
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2010
    Messages : 18
    Points : 12
    Points
    12
    Par défaut surcharge de modules
    Bonjour tout le monde !

    Un nouveau petit pb...

    Wouala, j'ai une arbo comme ci:

    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
    product/
    ----P1/
    --------sources/
    ------------main.py
    ------------...
    --------framework/
    ------------tools
    ----------------tools1.py
    ----------------tools3.py
    ----------------...
    ------------...
    ----P2/
    ----...
    ----framework/
    --------tools/
    ------------tools1.py
    ------------tools2.py
    ------------...
    --------...
    et (pour le dev de P1) un PYTHONPATH=.;product/P1/framework;product/framework

    je veux pouvoir ecrire dans les scripts de P1 :
    import tools => importe tools1 de P1/framework, tools2 et tools3 de product/framework
    ou from tools import * => idem
    ou encore from tools import tools1 => importe tools1 de P1/framework

    donc, priviligier les tools du produit P1 avant les tools génériques dans product/framework/...
    Sorte de surcharge de module en somme.


    J'ai idée qu'il faille construire cette surcharge dans le fichier product/P1/framework/tools/__init__.py qui doit prendre les tools locaux et completer par ceux de product/framework
    Mais ce n'est qu'une idée...

    Comment faites-vous cela ?

    Merci de vos réponses !

    Question subsidiaire : comment indenter un texte dans les questions sur le forums ?

  2. #2
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 389
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 389
    Points : 36 939
    Points
    36 939
    Par défaut globals et helpers
    Salut

    La question est vieille, mais à l'époque, je n'avais pas de solution sinon que çà me faisait penser au pattern de conception nommé 'strategy' mais pour ce qui est de le décliner dans ce contexte...

    La notion de ce pattern est de présenter au client une interface (au sens API) qui soit indépendante de sa réalisation. En général, çà se traduit par du vrai code... et je ne voyais pas trop comment Python permettait de réaliser cela "proprement".

    La solution repose sur la construction de "globals", i.e. le dictionnaire qui va permettre à l'interpréteur de donner un sens aux différents noms qu'il rencontrera lors de l'exécution de fonctions et autres méthodes.
    ...et...
    lorsqu'on fait import d'un module tutu, on pense plutôt aux fonctions qu'il contient et non à son "globals".

    Illustration

    Le module foo implémente la fonction bar. Pour l'utiliser, on pourra faire import foo, puis foo.bar()... c'est l'utilisation de "commune", de "base".

    Quand on ouvre foo.py, intuitivement on s'attend à trouver la définition de "bar", mais 'foo' est un petit malin: il ne contient que:
    from quux import *
    i.e. tous les symboles définis par "quux" sont placés dans le globals de "foo".
    => import foo récupère le "globals" de foo.

    Application pratique
    nota: empruntée à Pylons, qui l'a empruntée à Ruby On Rails, qui l'a promue avec le DRY: Don't Reinvent Yourself

    Lorsqu'on utilise des fonctions de différents modules, on se retrouve souvent à répéter des séries d'import ou de from... import... pour accéder à quelques fonctions très utilisées.

    Plutôt que de répéter la suite d'import et de from... import..., on les "factorise" via le globals d'un module "helpers.py" - qui peut contenir aussi de vraies fonctions - et on les utilise via un import helpers as h

    Pourquoi c'est "mieux"?
    A chaque import... et from... import... il va falloir récupérer les différents "globals" et les inclure dans le "globals" du module qu'on est en train de charger. Ici, on récupère le "globals" "prêt à l'emploi" du module helpers.
    => moins de lignes, plus rapide à charger...
    Pour le codeur, c'est moins de cut&paste mais aussi la possibilité de renommer les fonctions/objets les plus utilisés en h.machin plutôt qu'utiliser a.b.c.machin ici ou machin là bas.


    Et quel est le rapport avec la question posée...

    Ben, une fois compris qu'un import c'est l'intégration d'un globals dans le globals courant... la solution ici est de construire le "globals" avec ce qu'on veut dedans.

    Exemple :
    1 - from product.P1 import *
    2 - from product.P2 import *
    le (1) écrit le 'globals' avec les symboles de "product.P1" et (2) remplace leur définition avec les homonymes trouvés dans "product.P2"

    - W

  3. #3
    Expert confirmé 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
    Points : 4 005
    Points
    4 005
    Par défaut
    Bonjour,

    Désolé pilpoil123 mais je n'arrive pas à voir comment mettre en pratique la réponse de wiztricks.

    Bonjour wiztricks, il y a quelques concepts qui m'échappent dans ta réponse.

    Citation Envoyé par wiztricks Voir le message
    La solution repose sur la construction de "globals", i.e. le dictionnaire qui va permettre à l'interpréteur de donner un sens aux différents noms qu'il rencontrera lors de l'exécution de fonctions et autres méthodes.
    Plutôt que de répéter la suite d'import et de from... import..., on les "factorise" via le globals d'un module "helpers.py" et on les utilise via un import helpers as h

    on récupère le "globals" "prêt à l'emploi" du module helpers.
    Donc ce que tu préconise c'est la création d'un module (helpers.py pour ton exemple, tools.py pour pilpoil123) qui centraliserait les toolsxxx dans son dico globals ?

    Donc soit à chaque import toolsxxx tu rajoute ton toolsxxx dans le dico globals de 'tools' (utiliser le module imp pour remplacer l'import afin de copier dans le dico de tools si le module commance par 'tools' ? possible) ?
    Le globals de tools ne contient donc que les toolsxxx déjà importés une fois.
    Comment rajouter un objet sans en faire l'import ? Aucune idée.

    Faire un premier import de tous les toolsxxx pour identifier les objets ?
    Du style tools.py contient tous les imports toolsxxx et on l'importe une première fois pour remplir son globals.
    Idée loufoque non ?

    Dans le cadre de la question de pilpoil123 :
    Citation Envoyé par pilpoil123 Voir le message
    je veux pouvoir ecrire dans les scripts de P1 :
    import tools => importe tools1 de P1/framework, tools2 et tools3 de product/framework
    ou from tools import * => idem
    ou encore from tools import tools1 => importe tools1 de P1/framework
    Je ne vois pas comment faire cela avec le globals sans import préalable.

    Comme tu le vois ta réponse me dépasse (pour changer... ).
    Une petite explication supplémentaire ?

    Sinon, comme je le signale plus haut, pourquoi ne pas utiliser le module imp pour remplacer import. Il suffit de lui donner une liste des toolsxxx.py et lorque l'import comporte un from tools import toolsxxx ou import tools imp importe le ou les modules depuis la liste. Il existe pas mal d'exemples sur le remplacement de l'import avec imp sur le web.

    Dans l'attente de te lire wiztricks et encore désolé pilpoil123 mais j'ai parfois du mal à voir ce que j'ai sous le nez.

    Par contre comme le sujet date tu as sans doute rechercher de ton coté pilpoil123. As tu trouvé une solution ?

    @+

  4. #4
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 389
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 389
    Points : 36 939
    Points
    36 939
    Par défaut
    Salut PauseKawa

    __dict__ est si compliqué

    Une arborescence de modules telle que décrite par pilpoil123 est "matériellement" représentée par une arborescence équivalente de répertoires et de scripts.

    Lorsque l'interpréteur rencontre le premier "import toto", c'est l'arborescence fichiers qui lui permet lui permet de savoir quoi faire pour:
    1 - construction du globals de toto et,
    2 - insertion de celui ci dans la clé toto du "globals" courant.
    Les "import toto" suivants pourront sauter "1".

    Penser globals est __dict__ !!

    Ou quelques bévues lorsqu'on l'oublie:

    from toto import *
    Dans ce cas, on inclue dans le globals courant le contenu du globals de "toto". Imaginons que toto.py contienne a = 'x': çà crée la clé "a" avec dedans 'x'. Si plus tard, toto.a est modifié, le contenu de "a" dans les "globals" construits via "from toto import *" restera à sa valeur initiale.
    C'est la raison pour laquelle, il est préférable de faire import toto...(dans ce cas).

    logging
    C'est un module simple à utiliser... mais qui défie l'entendement si on oublie globals. Simple à utiliser: au début de chaque script on peut mettre:
    import logging
    log = logging.getLogger(__name__) #ou autre
    Lorsque l'interpréteur chargera les différents scripts nous allons avoir les différents 'log' initialisés sans soucis tant qu'il ne rencontre pas un log.xxx(...): il faudra avoir initialisé logging "avant" pour que les messages sortent.

    Revenons à notre arborescence de modules matérialisée par l'arborescence fichiers/scripts.
    Supposons le répertoire /a/b/c
    Dans un file system, il est "possible" de créer "links" que nous allons placer dans un "vrai" répertoire. Appelons le "helpers", on peut y mettre un link foo vers /a/b/c de sorte que /helpers/foo retourne le contenu de /a/b/c.
    On peut aussi y mettre un fichier tutu ou un sous-repertoire
    En fait, on peut y mettre ce qu'on veut en lui donnant le nom qu'on veut.

    helpers.py ou helpers/__init__.py a la même fonction: il construit un globals avec des noms et des définitions qui peuvent être réalisées "ailleurs" - les links - ou "dedans".
    Et puisque le globals de helpers est construit dynamiquement, ce qui sera dedans comme noms ou comme définition est hyper flexible.

    Je ne vois pas comment faire cela avec le globals sans import préalable. Comme tu le vois ta réponse me dépasse (pour changer... ).
    Une petite explication supplémentaire ?
    Tu oublies peut être que chaque script/module a son propre "globals".
    Lorsque l'interpréteur lit foo.py et y rencontre "import bar"...
    • Il est en train de construire le globals de foo,
    • import bar, lui dit récupère le globals de bar et met le dans la clé "bar"

    Comment construire le globals de "bar" lui est indiqué par le contenu de bar.py ou de bar/__init__.py, mais ce globals a peut être déjà été construit plus tôt... Dans ce cas, il a moins de boulot.
    => lorsqu'il y a des boucles, style impossible de construire le globals de bar sans avoir construit celui de foo: çà plante...

    Sinon, comme je le signale plus haut, pourquoi ne pas utiliser le module imp pour remplacer import.
    Faire un import custom ou utiliser les fonctionnalités qu'offre import?

    Parfois on part billes en tête en se disant qu'en codant soi-même le truc qu'on veut çà ira plus vite plutôt que d'essayer de comprendre ce qu'on a déjà. Pire, on est tellement obnubilé par son idée qu'on ne regarde pas ce qu'on a déjà sous l'angle qu'il faudrait et on bricole un truc qui le fait.

    Cette approche a pour qualité de livrer "vite" mais le défaut d'être difficile à maintenir car plus de lignes de codes, tests, défauts et de documentation,
    Souvent nous n'avons pas le temps de choisir et donc tant pis: c'est la vie.

    Ici, nous ne sommes pas dans ce genre de tempête. Nous pouvons essayer de prendre le temps pour confronter nos expériences et différentes approches d'un même problème pour, à défaut d'avoir eu la possibilité de l'intégrer dans le code qu'on a déjà livré, améliorer ses connaissances du langage et des possibilités qu'il nous offre.

    Maintenant, je ne prétends pas que pour le problème posé mon approche soit "la meilleure", il est possible qu'il faille se résoudre à utiliser imp.

    Ceci dit, ces histoires de "globals" sont tellement surprenantes et déconcertantes que j'espère que l'éclairage que j'y apporte permettra (à ceux qui auront eu la patience de me lire) d'en tirer profit.
    - W

  5. #5
    Expert confirmé 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
    Points : 4 005
    Points
    4 005
    Par défaut
    Bonsoir,

    Bien qu'après relecture mon texte ne le montre pas j'avais bien compris le principe des globals et autre.

    En fait pour mes questions j'ai surtout regarder :
    Citation Envoyé par pilpoil123 Voir le message
    et (pour le dev de P1) un PYTHONPATH=.;product/P1/framework;product/framework

    je veux pouvoir ecrire dans les scripts de P1 :
    import tools => importe tools1 de P1/framework, tools2 et tools3 de product/framework
    ou from tools import * => idem
    ou encore from tools import tools1 => importe tools1 de P1/framework

    donc, priviligier les tools du produit P1 avant les tools génériques dans product/framework/...
    Sorte de surcharge de module en somme.
    Cela implique soit une énumération des 'tools' (dans une liste ou un dico par exemple) soit un accès aux sous répertoires comme le fait pkgutil avec extend_path.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    from pkgutil import extend_path
    __path__ = extend_path(__path__, __name__)
    Toutefois pilpoil123 demande a pouvoir faire un import tools alors que tools n'existe pas.
    Avec pkgutil le problème reste entier puisque cela ne fais que prendre en compte les sous répertoires.
    imp présente un intérêt dans ce cas avec .new_module.

    Citation Envoyé par wiztricks Voir le message
    Faire un import custom ou utiliser les fonctionnalités qu'offre import?
    Loin de moi cette idée (j'y ai laisser assez de plumes).
    En fait c'est de tester les imports : Si c'est un import 'normal' on utilise __import__ sinon on utilise imp.
    Vite fais cela donne quelque chose comme :
    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
    #!/usr/bin/env python
    # -*- coding: UTF-8 -*-
    #
    #
    import imp
    import sys
    import __builtin__
    import os.path
     
    toolsdic={'newvar': '/home/patrice/Bureau/code/newvar'}
     
    def import_replacement(name, globals=None, locals=None, fromlist=None):
        if name in sys.modules:
            module = sys.modules[name]
        elif name == 'tools':
                if not fromlist:
                    # A faire
                    sys.exit(1)
                elif '*' in fromlist:
                    # revoir
                    for elem in toolsdic:
                        print 'import de :', name
                        modulepath, modulename = os.path.split(toolsdic[elem])
                        file, filename, data = imp.find_module(modulename, [modulepath])
                        module = imp.load_module(elem, file, filename, data)
                else:
                    #for elem in fromlist:
                    # A faire
                    sys.exit(1)
        else:
            print 'import de :', name
            module = real_import(name)
        return module
     
    real_import = __builtin__.__import__
    __builtin__.__import__ = import_replacement
     
    #import Tkinter
    #if 'Tkinter' in sys.modules: print "dans sys.modules", sys.modules['Tkinter']
    #import newvar
    # Variantes d'import
    from tools import *
    #import tools
    print sys.modules['newvar']
    #from tools import newvar
    #if 'tools' in sys.modules: print 'ok'
    #print sys.modules['tools']
    from re import *
    if 're' in sys.modules: print 'ok'
    print sys.modules['re']
    + création du module tools si besoin (voir plus haut).
    Bon, le code est bien sur à revoir complètement mais je ne pense pas refaire le monde avec cela.

    Ceci dit je retourne étudier l'idée de helpers.

    Bon code à tous

    Edit : module = imp.load_module(elem, file, filename, data) et non name bien sur.

  6. #6
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 389
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 389
    Points : 36 939
    Points
    36 939
    Par défaut
    Salut,

    Dois je comprendre que le passage par imp se justifierait par une interprétation du cas d'utilisation nécessitant la création d'un module "tools" à partir de rien - i.e. sans tools.py -?

    Tu as peut être raison.

    Car de mon côté, lisant:
    je veux pouvoir ecrire dans les scripts de P1 :
    import tools => importe tools1 de P1/framework, tools2 et tools3 de product/framework
    ou from tools import * => idem
    ou encore from tools import tools1 => importe tools1 de P1/framework
    J'ai compris "que faut-il mettre dans tools.py pour que..." sans même imaginer qu'il fallait, en plus, se passer de tools.py...

    Car, avec ou sans tools.py, la question n'est-elle pas d'avoir la possibilité de remonter à l'appelant un "globals" construit bizarrement?

    Laissons l'auteur nous donner un indice.

    - W

Discussions similaires

  1. [VB.NET] Surcharge fonction dans un module ?
    Par Jean-Philippe André dans le forum Débuter
    Réponses: 3
    Dernier message: 31/05/2012, 17h21
  2. [Drupal] Surcharger un module
    Par baggie dans le forum EDI, CMS, Outils, Scripts et API
    Réponses: 4
    Dernier message: 26/03/2012, 09h37
  3. [Drupal] Surcharger fonction d'un autre module
    Par dubitoph dans le forum EDI, CMS, Outils, Scripts et API
    Réponses: 0
    Dernier message: 13/09/2011, 19h04
  4. PerlDoc sur une fonction d'un module
    Par lesouriciergris dans le forum Modules
    Réponses: 2
    Dernier message: 13/03/2003, 20h50
  5. Réponses: 8
    Dernier message: 20/11/2002, 11h50

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