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 l'opérateur =


Sujet :

Python

  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Février 2005
    Messages
    130
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 130
    Par défaut Surcharge de l'opérateur =
    Bonjour à tous,

    Est-il possible de surcharger l'opérateur = en python ?
    J'arrive à tout redéfinir, à peu près, suite à la lecture de http://www.python.org/doc/2.4.1/lib/operator-map.html
    Mais rien sur l'opérateur =, et malheureusement c'est celui là dont j'ai besoin.

    Avez-vous une idée?

    Merci d'avance

  2. #2
    Membre émérite

    Profil pro
    Inscrit en
    Août 2004
    Messages
    723
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2004
    Messages : 723
    Par défaut
    Il ne t'est pas possible de faire autrement ?
    Ça m'étonnerait qu'on puisse le surcharger.

  3. #3
    Membre éprouvé
    Profil pro
    Inscrit en
    Septembre 2007
    Messages
    105
    Détails du profil
    Informations personnelles :
    Âge : 57
    Localisation : Suisse

    Informations forums :
    Inscription : Septembre 2007
    Messages : 105
    Par défaut
    Bonjour,

    Si j'ai bien compris ta question, tu désires définir l'opération d'affectation pour un objet.

    Si c'est le cas, pour les attributs il faut utiliser la méthode __setattr__. Sinon, je te propose de regarder du côté de la méthode __set__.

    Tu trouveras plus d'information dans la doc de Python.

    Salutations.

  4. #4
    Membre confirmé
    Profil pro
    Inscrit en
    Février 2005
    Messages
    130
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 130
    Par défaut
    arf... je n'ai pas vraiment le choix...
    A vrai dire, je dois pouvoir utiliser une classe comme si c'était un type prédéfini, comme un float ou un int par exemple.

    Par exemple:
    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
     
    class BaseTypeWrapper (object):
        def __init__ (self):
            self.value = None
     
        def __eq__(self, val):
            return self.value == val
     
        def __add__(self, val):
            return self.value + val
     
        def __sub__(self, val):
            return self.value - val
     
        def __mul__(self, val):
            return self.value * val
     
        def __div__(self, val):
            return self.value / val
    Il ne me manque que l'opérateur =, pour éviter qu'à chaque affectation, Python me transformer le type de mon instance dans le type affecté:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    test =  BaseTypeWrapper()
    test=5
    => test est du type int, alors que je veux qu'il soit du type BaseTypeWrapper, avec self.value == 5


    Il n'existe pas un workaround pour faire ce genre de chose ?



    EDIT: __set__ a l'air de ressembler à ce que je cherche, mais je n'étais pas tombé dessus, je n'ai vu que les autres... Je vais essayer de comprendre comment ça s'utilise, ca ne me parle pas plus que ça, de visu.

  5. #5
    Membre confirmé
    Profil pro
    Inscrit en
    Février 2005
    Messages
    130
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 130
    Par défaut
    Bon c'est malheureux, mais je ne comprends pas du tout comment peut fonctionner __set__ et __get__
    J'ai pourtant fait correctement fonctionner l'exemple ici: http://users.rcn.com/python/download/Descriptor.htm
    Par contre, lorsque je tente de faire de même avec mon objet, l'opérateur égal n'est pas surchargé:

    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
     
    class Observable (object):
        def __init__ (self):
            self.value = None
            self.subscribers = []
     
        def __set__ (self, obj, val):
            self.value = val
            for subscriber in self.subscribers:
                subscriber.set(val)
     
        def __get__(self, obj, val):
            return self.value
     
        def __eq__(self, val):
            return self.value == val
     
        def __add__(self, val):
            return self.value + val
     
        def __sub__(self, val):
            return self.value - val
     
        def __mul__(self, val):
            return self.value * val
     
        def __div__(self, val):
            return self.value / val
     
     
        def subscribe (self, subscriber):
            self.subscribers.append(subscriber)
    Cas prouvant que l'opérateur n'est pas surchargé:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    bob = Observable()
    bob = 5
    print type(bob)
    Sortie:

  6. #6
    Membre éprouvé
    Profil pro
    Inscrit en
    Septembre 2007
    Messages
    105
    Détails du profil
    Informations personnelles :
    Âge : 57
    Localisation : Suisse

    Informations forums :
    Inscription : Septembre 2007
    Messages : 105
    Par défaut
    Bonjour,

    Je crois que c'est normal, car tu veux modifier un attribut et dans ce cas il faut utiliser __setattr__.

    Voici un exemple d'implémentation d'un pseudo type constant:
    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
    #!/usr/bin/python
    # -*- coding:Utf-8 -*-
     
    class Const:
     
        def __init__ (self):
            Const.__items = {}
     
        def __getattr__ (self, attr):
            try:
                return Const.__items [attr]
            except:
                return self.__dict__ [attr]
     
        def __setattr__ (self, attr, value):
            if attr in Const.__items.keys ():
                raise "Impossible de réassigner la valeur (%s) Ã* la constante: %s" % (value, attr)
            else:
                Const.__items [attr] = value
     
        def __str__ (self):
            return "\n".join (["%s: %s" % (str (k), str (v)) for k,v in Const.__items.items ()])
     
    if __name__ == "__main__":
        import sys
     
        MyConst = Const ()
        MyConst.spam = 2
        MyConst.eggs = 3
        try:
            MyConst.spam = 1
        except:
            print sys.exc_info()[0]
        print "MyConst.spam = %s\nMyConst.eggs = %s" % (MyConst.spam, MyConst.eggs)
    Salutations.

  7. #7
    Membre confirmé
    Profil pro
    Inscrit en
    Février 2005
    Messages
    130
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 130
    Par défaut
    En fait, ce que je veux, c'est utiliser mon type Observable comme si c'était un type prédéfini (je cache le fait que ce soit une classe qui enrobe une valeur).
    Je veux donc vraiment y accéder de la même manière qu'un int, à savoir comme je le fais plus haut dans mon test qui marche pas.

    Si j'utilise __getattr__ et __setattr__, je vais être obligé d'appeler explicitement bob.value = 5 (dans mon exemple), et non pas seulement bob=5.

  8. #8
    Membre confirmé
    Profil pro
    Inscrit en
    Février 2005
    Messages
    130
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 130
    Par défaut
    Ôte moi d'un doute:
    The following methods only apply when an instance of the class containing the method (a so-called descriptor class) appears in the class dictionary of another new-style class, known as the owner class. In the examples below, “the attribute” refers to the attribute whose name is the key of the property in the owner class’ __dict__. Descriptors can only be implemented as new-style classes themselves.
    J'espère que cela ne signifie pas que l'opérateur= ne peut être appelé que sur un attribut d'une classe qui dérive de object => il serait impossible de redéfinir cet opérateur, pour ensuite s'en servir localement dans une fonction, par exemple ?

  9. #9
    Membre éprouvé
    Profil pro
    Inscrit en
    Septembre 2007
    Messages
    105
    Détails du profil
    Informations personnelles :
    Âge : 57
    Localisation : Suisse

    Informations forums :
    Inscription : Septembre 2007
    Messages : 105
    Par défaut
    Bonjour,

    Bien vu, là du coup ça me dépasse.
    Car dans ce cas ta variable est en premier lieu une instance de ta classe Observable(), puis un entier.

    Normalement en Python, l'utilisateur peut tout faire ( et n'importe quoi aussi :/ ).
    Ainsi, cela doit être possible, mais je crains qu'il faille redéfinir l'opération d'affectation pour tout les types de base (<- là suis pas sur et je fais de l'extrapolation).

    Ceci dit, il me semble que la piste que tu veux suivre est compliquée.
    Pour ma part, dès que je réalise ce que je viens d'écrire précédemment, je me pose la question suivante: est que c'est le bon choix pour résoudre mon problème actuel (et du coup je remet en doute tout mon algo).

    Attention, je ne dis pas que c'est ce qu tu dois faire. Mais es tu sur que c'est le seul moyen d'atteindre ton objectif ou es ce que c'est plus un choix cosmétique et/ou intellectuel ?

    Quoi qu'il en soit, je suis désolé mais je ne peux pas t'aider d'avantage.

    Salutations.

  10. #10
    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
    bonjour,

    c'est normal. il y a une différence entre l'exemple que tu as trouvé et ton propre cas.

    dans l'exemple, il s'agit de redéfinir l'opérateur "=" pour des attributs d'un objets.
    dans ton cas, il n'y a pas d'attribut d'objet mais simplement un objet.

    en gros, tu peux redéfinir l'opérateur "=" si tu es dans un cas comme ceci:

    toto.value = 5

    mais pas si tu veux faire ça:

    toto = 5

    Dans le premier cas, l'opérateur "=" dépend de la nature de l'objet toto, dans le second, l'opérateur "=" échappe à ce type de contrôle.

    C'est vrai que c'est quelque chose que l'on fait dans d'autres langages comme le c++ mais il y a une différence fondamentale avec python. L'opérateur "=" n'a pas du tout la même signification ceci est lié notamment au fait que python est un langage à typage dynamique.

    en c++, d'abord on déclare le type de l'objet. du coup, lorsqu'on utilise l'opérateur =, c'est l'opérateur = de l'objet crée qui est invoqué.

    en python, quand on utilise l'opérateur =, il est résolu dans un contexte d'un niveau supérieur à l'objet:

    toto = 5

    python fait ceci:

    - il alloue un espace mémoire pour contenir un entier
    - il crée un lien entre le nom "toto" et l'adresse mémoire (le pointeur) de l'entier

    du coup, le nom "toto" peut être utilisé pour référencer d'autres pointeurs que le pointeur qu'il référençait initialement (ce qui peut entrainer le ramasse miettes sur une adresse mémoire qui n'est plus référencée).

    pour résumer, en c++ quand on utilise l'opérateur = on sait déjà quel type d'objet on manipule. en python, on ne connait le type de l'objet qu'après l'appel de l'opérateur "=".

    pour pouvoir faire ce que tu souhaites, il faudrait passer par un autre opérateur que "=" ou alors utiliser une méthode "set".

    à moins de changer le contexte et de travailler sur des attributs de classe.

    j'espère que j'ai pas été trop brouillon dans mon explication.

  11. #11
    Membre éprouvé
    Profil pro
    Inscrit en
    Septembre 2007
    Messages
    105
    Détails du profil
    Informations personnelles :
    Âge : 57
    Localisation : Suisse

    Informations forums :
    Inscription : Septembre 2007
    Messages : 105
    Par défaut
    Re,
    Comme j'aime pas donner ce genre de réponse (cf. mon post précédent), j'ai fait un petit coup de
    Il semblerait que la méthode magique soit: __eq__

    Je te laisse voir l'article: de la faq python du site Developpez.com

    Salutations.

    Edit: J'ai lu en travers et la méthode __eq__ correspond à l'opérateur == :/
    Mais quoi qu'il en soit il est bien écrit: "En Python, on peut surcharger tous les opérateurs."

  12. #12
    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
    Citation Envoyé par aepli Voir le message
    Re,
    Comme j'aime pas donner ce genre de réponse (cf. mon post précédent), j'ai fait un petit coup de
    Il semblerait que la méthode magique soit: __eq__

    Je te laisse voir l'article: de la faq python du site Developpez.com

    Salutations.
    attention, il s'agit ici de l'opérateur "==" (comparaison) et non de l'opérateur "=" (affectation).

  13. #13
    Membre confirmé
    Profil pro
    Inscrit en
    Février 2005
    Messages
    130
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 130
    Par défaut
    Ok très bonne explication, c'est très clair pour moi... Par contre, je suis extrêmement embêté car cette impossibilité de Python va me forcer à aller modifier environ 400 affectations dans mon projet...

    Tu connaîtrais pas un contournement, par hasard, qui permettrait de simuler la surcharge de l'opérateur = pour un objet ?


  14. #14
    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
    non malheureusement :/

    si ce n'est détourner un opérateur différent, par exemple "<<" pour l'utiliser dans le contexte que tu souhaites:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    bob = Observable()
    bob << 5
    mais je suis pas très fan (ça tu t'en moques certainement :p)

  15. #15
    Membre confirmé
    Profil pro
    Inscrit en
    Février 2005
    Messages
    130
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 130
    Par défaut
    Citation Envoyé par aepli Voir le message
    Mais es tu sur que c'est le seul moyen d'atteindre ton objectif ou es ce que c'est plus un choix cosmétique et/ou intellectuel ?
    J'avais pas vu ce post là.
    En fait, je vais t'expliquer ma problématique, tu verras peut être mieux ce dont j'ai besoin (il existe peut être quelque chose qui pourrait me sauver en Python, et moi venant de C++/Java, je ne pense pas forcément à la bonne solution).
    En gros, j'ai de nombreux modules de calculs, qui utilisent des paramètres de types basiques: int, float, string, etc... J'ai besoin de rajouter, pour chacun de ces paramètres, un aspect Observable, qui soit le plus transparent possible, et qui me permette de faire en sorte que lorsque ce paramètre est modifié lors d'une affectation, des souscripteurs en soient notifiés. C'est pour cela que j'ai créé une classe Observable, contenant juste un attribut et une liste de souscripteurs, et pour laquelle je voulais redéfinir l'opérateur =, afin de transférer le = vers l'attributde la classe.
    Ca me permettait de rajouter l'Observable sur des types prédéfinis, sans avoir à modifier toutes les affectations de tous les paramètres dans mon code.

  16. #16
    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
    je peux te proposer une implémentation utilisant property:

    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
    class Observable (object):
        def __init__ (self):
            self._value = None
            self.subscribers = []
     
        def set_value (self, val):
            self._value = val
            for subscriber in self.subscribers:
                subscriber.set(val)
     
        def get_value(self):
            return self._value
     
        value = property(fset=set_value,fget=get_value)
     
        def __eq__(self, val):
            return self._value == val
     
        def __add__(self, val):
            return self._value + val
     
        def __sub__(self, val):
            return self._value - val
     
        def __mul__(self, val):
            return self._value * val
     
        def __div__(self, val):
            return self._value / val
     
        def subscribe (self, subscriber):
            self.subscribers.append(subscriber)
    Ce code devrait alors "notifier" correctement tes souscripteurs:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    o = Observable()
    o.value = 3.14
    o.subscribe(toto)
    o.value = 6.28

  17. #17
    Membre confirmé
    Profil pro
    Inscrit en
    Février 2005
    Messages
    130
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 130
    Par défaut
    Yes, cette solution me semble vraiment pas mal. Je testerai ça direct lundi en arrivant au boulot, mais c'est clair que ça semble vraiment coller à mon problème.
    Merci, je passe ça en résolu dès que j'ai testé

    EDIT: cf post suivant

  18. #18
    Membre confirmé
    Profil pro
    Inscrit en
    Février 2005
    Messages
    130
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 130
    Par défaut
    Le problème, c'est que la propriété "value" doit être appelée en appelant explicitement value, comme si j'avais fait un getter et un setter. Est-il possible d'appeler cette propriété en faisant passer le nom de l'instance (pour simuler l'opération = sur l'instance de cette classe) ?

  19. #19
    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
    bonjour,

    je crains que non, mais si je me trompe je suis fortement intéressé par la solution.

  20. #20
    Membre confirmé
    Profil pro
    Inscrit en
    Février 2005
    Messages
    130
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 130
    Par défaut
    arf... J'ai été trompé par http://python.developpez.com/faq/?page=Objet: "En Python, on peut surcharger tous les opérateurs."

    Bon, je suis bon pour aller modifier les affectations sur toutes les variables concernées par cette classe :'(

Discussions similaires

  1. Surcharge de l'opérateur new
    Par :Bronsky: dans le forum C++
    Réponses: 17
    Dernier message: 27/10/2010, 21h33
  2. Réponses: 8
    Dernier message: 29/08/2006, 00h56
  3. [C#] Surcharge de l'opérateur ==
    Par LE NEINDRE dans le forum Windows Forms
    Réponses: 3
    Dernier message: 18/07/2006, 16h19
  4. Réponses: 6
    Dernier message: 12/07/2006, 15h34
  5. Réponses: 15
    Dernier message: 25/01/2005, 16h51

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