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

Bibliothèques tierces Python Discussion :

Ajout de méthodse à une classe sans dérivation


Sujet :

Bibliothèques tierces Python

  1. #1
    Membre expérimenté Avatar de plxpy
    Homme Profil pro
    Ingénieur géographe
    Inscrit en
    Janvier 2009
    Messages
    792
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 59
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur géographe
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Janvier 2009
    Messages : 792
    Points : 1 481
    Points
    1 481
    Par défaut Ajout de méthodse à une classe sans dérivation
    Bonjour

    Est-il possible, en Python, d'ajouter des méthodes à une classe MAIS :
    1 - sans modifier le source de la classe
    2 - sans la dériver
    (comme le permet Objective C par exemple avec ses "extensions" de classe)

    Ces contraintes viennent du fait que :

    - j'utilise un package que je ne maintiens pas moi-même et de nouvelles versions/révisions sont régulièrement disponibles : je ne souhaite pas modifier ce source (qui de plus fait essentiellement un interfaçage avec une bibliothèque C/C++ et dont le code est - disons - "velu")

    - les objets de ces différentes classes "de base" se contiennent un peu les uns les autres, de nombreuses méthodes retournent des objets de ces classes, bref : une dérivation n'est pas satisfaisante
    "La simplicité ne précède pas la complexité, elle la suit." - Alan J. Perlis
    DVP ? Pensez aux cours et tutos, ainsi qu'à la FAQ !

  2. #2
    Membre émérite
    Avatar de DelphiManiac
    Homme Profil pro
    Homme à tout faire
    Inscrit en
    Mars 2002
    Messages
    1 147
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Homme à tout faire
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2002
    Messages : 1 147
    Points : 2 533
    Points
    2 533
    Par défaut
    A priori oui, mais ça demande tout de même des tests plus évolués que les 20 lignes de codes que je viens de pondre !!

    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
    #!/usr/bin/python
    # -*- coding: utf-8 -*-
     
    class Test():
        def test(self):
            self.mavar = 12
            print self
            print 'coucou'
     
    def test1(self):
        print self
        print self.mavar
        print 'coucou1'
     
    Test.__dict__['test1'] = test1
     
    t = Test()
    t.test()
    t.test1()
    Si ce message vous a semblé utile, il est possible qu'il soit utile à d'autres personnes. Pensez au . Et n'oubliez pas le le moment venu !

    On n'a pas à choisir si l'on est pour ou contre la décroissance, elle est inéluctable, elle arrivera qu'on le veuille ou non.

  3. #3
    Membre expérimenté Avatar de plxpy
    Homme Profil pro
    Ingénieur géographe
    Inscrit en
    Janvier 2009
    Messages
    792
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 59
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur géographe
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Janvier 2009
    Messages : 792
    Points : 1 481
    Points
    1 481
    Par défaut
    Merci pour la réponse : s'attaquer au __dict__ de la classe ne me serait pas venu à l'esprit.

    Effectivement, il faut creuser un peu plus avec des classes un peu plus "péchues" (celles que j'utilise sont issues du package Shapely qu'on trouve dans les packages de www.python.org et qui manipulent des objets géométriques).

    Je creuse un peu plus pour vérifier que ce n'est pas trop "effet de bord" et je rajouterai un message avec mes conclusions.

    Encore merci
    "La simplicité ne précède pas la complexité, elle la suit." - Alan J. Perlis
    DVP ? Pensez aux cours et tutos, ainsi qu'à la FAQ !

  4. #4
    Membre expérimenté Avatar de plxpy
    Homme Profil pro
    Ingénieur géographe
    Inscrit en
    Janvier 2009
    Messages
    792
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 59
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur géographe
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Janvier 2009
    Messages : 792
    Points : 1 481
    Points
    1 481
    Par défaut
    Même avec des classes un peu plus fournies, ça marche.

    J'ai trouvé un article qui approfondit le sujet en passant, entre autres, par des décorateurs. Je ne suis pas allé jusqu'à lire toutes les solutions proposées ni à les tester.

    Je ne reprends pas l'ensemble des tests effectués avec les classes que j'utilise mais voici un squelette de trois modules (je n'attaque pas directement __dict__ mais utilise la fonction setattr):

    Le module chargé d'ajouter les méthodes (extra.py):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    from module import MaClasse
     
    def add_method(cls,**kw):
        for k,v in kw.iteritems():
            setattr(cls,k,v)
     
    def coucou(comme_self,texte=""):
        print "AJOUTE",texte
     
    add_method(MaClasse,methode_ajoutee=coucou)
    Le module avec le code de la classe (module.py) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    class MaClasse():
     
        def methode_de_base(self,texte=""):
            print "methode_de_base",texte
    et enfin , l'application main.py

    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
     
    from module import MaClasse
    import extra
     
    class MaClasseDeriveeSimple(MaClasse):
        pass
     
    class MaClasseDeriveeSurcharge(MaClasse):
        def methode_ajoutee(self,texte=""):
            print "SURCHARGE",texte
     
    objetBasique = MaClasse()
    objetBasique.methode_de_base("objet de la classe")
    objetBasique.methode_ajoutee("objet de la classe")
     
    objetDerive = MaClasseDeriveeSimple()
    objetDerive.methode_de_base("objet de la classe derivee")
    objetDerive.methode_ajoutee("objet de la classe derivee")
     
    objetSurcharge = MaClasseDeriveeSurcharge()
    objetSurcharge.methode_de_base("objet de la classe derivee avec surcharge")
    objetSurcharge.methode_ajoutee("objet de la classe derivee avec surcharge")
    On voit de plus que, si on dérive la classe, la méthode ajoutée est encore disponible. On peut même, dans une autre dérivation, la surcharger.

    Reste à espérer que ce n'est pas un effet de bord/une faille qui pourrait disparaitre avec les nouvelles versions. Un cas analogue est relaté dans un mail de van Rossum (himself) et, j'ai testé, en 2.5, ça ne fonctionne plus. Mais là, il s'agissait d'ajouter une méthode à la classe prédéfinie str.

    Dans mon cas, cette solution apporte suffisamment de souplesse, sans être trop "tordue", pour que je prenne le risque.
    "La simplicité ne précède pas la complexité, elle la suit." - Alan J. Perlis
    DVP ? Pensez aux cours et tutos, ainsi qu'à la FAQ !

  5. #5
    Membre émérite
    Avatar de DelphiManiac
    Homme Profil pro
    Homme à tout faire
    Inscrit en
    Mars 2002
    Messages
    1 147
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Homme à tout faire
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2002
    Messages : 1 147
    Points : 2 533
    Points
    2 533
    Par défaut
    De mon coté, j'ai continué à jouer un peu avec les dict. On peut par ce biais remplacer une classe existante par la sienne sans pour autant en dériver.

    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
    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    
     
    class ClasseOrigine:
        def __init__(self):
            print 'in init de ClasseOrigine'
            
        def test(self):
            self.mavar = 12
            print 'in test de ClasseOrigine'
     
    def test1(self):
        print u'in test1 ajouté à Classe origine %s avec comme valeur de mavar :' % self.mavar
    
    class NouvelleClasse(object):
        def __new__(cls):
            print 'in new de NouvelleClasse'
            return oldClasseOrigine()
    
    ClasseOrigine.__dict__['test1'] = test1
    globals()['oldClasseOrigine'] = globals()['ClasseOrigine']
    globals()['ClasseOrigine'] = NouvelleClasse
    
    
    t1 = ClasseOrigine() # on ne créer pas directement une instance de la classe
    print t1
    t1.test()
    t1.test1()
    Ici quand je fais t1 = ClasseOrigine(), je ne créer pas directement une instance de la classe, mais j'appelle indirectement le __new__ de NouvelleClasse(), ce qui me permets de changer tout ce que je veux avant l'instantation de la classe réelle.

    Tout code existant précédemment, et dont on a pas la maitrise, passera automatiquement par notre NouvelleClasse sans qu'il le sache.
    Si ce message vous a semblé utile, il est possible qu'il soit utile à d'autres personnes. Pensez au . Et n'oubliez pas le le moment venu !

    On n'a pas à choisir si l'on est pour ou contre la décroissance, elle est inéluctable, elle arrivera qu'on le veuille ou non.

  6. #6
    Membre éclairé
    Homme Profil pro
    heu...
    Inscrit en
    Octobre 2007
    Messages
    648
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : heu...

    Informations forums :
    Inscription : Octobre 2007
    Messages : 648
    Points : 773
    Points
    773
    Par défaut
    Comme Delphimaniac viens de me poster un lien vers ce sujet qui traite ce que je recherchais, je poste une autre solution :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    >>> class A(object): pass
    >>> import re
    >>> def meth(className,functionCode):
        comp=re.compile('def (.*?)\(')
        methodName=comp.findall(functionCode)[0]
        exec(functionCode)
        exec("setattr({0},'{1}',classmethod({1}))".format(className,methodName))
    >>> meth('A','def foo(cls): print "foo"')
    >>> A.foo()
    foo
    Bon, on peut raccourcir évidement la fonction en virant comp, methodName et en ne faisant qu'un seul exec, mais j'avais la flemme...

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Declarer un pointeur vers une classe sans new ?
    Par zi_omnislasher dans le forum C++
    Réponses: 15
    Dernier message: 28/09/2007, 11h51
  2. [DOM] Ajouter un event à une class
    Par Nulenprogra dans le forum Général JavaScript
    Réponses: 23
    Dernier message: 30/04/2007, 11h19
  3. Réponses: 1
    Dernier message: 17/04/2007, 16h51
  4. [C#] Une classe sans constructeur, ca existe?
    Par legillou dans le forum Windows Forms
    Réponses: 4
    Dernier message: 03/07/2006, 09h58
  5. Réponses: 11
    Dernier message: 16/10/2005, 20h21

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