Partage
  • Partager sur Facebook
  • Partager sur Twitter

utilisation des propriétés

Sujet résolu
    1 février 2013 à 16:48:18

    Bonjour à tous,

    Je découvre depuis peu le langage Python. Ma question va donc vous semblé basique mais j'ai bien comparé mon code à celui du tuto et je ne vois pas pourquoi le mien ne fonctionne pas.

    Tout d'abord le code :

    class Plateau:
        def __init__(self):
            self._position = 0
    
        def _get_position(self):
            print "dans le get: "
            return self._position
    
    
        def _set_position(self, deplacement):
            print "dans le set: "
            self._position = deplacement + self._position
           
        position = property(_get_position,_set_position)
    
    
    if __name__ == '__main__':	       
    p = Plateau()
    p.position = random.randrange(1,6)
    str(p.position)

    le problème étant que je ne passe à priori jamais dans mon _get et _set puisque je n'obtiens aucun message

    Pour infos, je travail en python 2.5, peut être est-ce une explication ?

    Merci d'avance

    -
    Edité par Angelo 1 février 2013 à 17:05:38

    • Partager sur Facebook
    • Partager sur Twitter
      1 février 2013 à 19:24:46

      Pour infos, je travail en python 2.5, peut être est-ce une explication ?

      Oui. En Python2, les properties ne fonctionnent que sur des classes "new-style". En ce qui concerne ton code, il suffit de faire dériver explicitement ta classe Plateau de object :

      class Plateau(object):
      
      • Partager sur Facebook
      • Partager sur Twitter
      Zeste de Savoir, le site qui en a dans le citron !
        3 février 2013 à 12:21:38

        Note que tu peux légèrement améliorer la lisibilité en utilisant property en décorateur :

        class Plateau(object):
            def __init__(self):
                self._position = 0
        
            @property
            def position(self):
                print "dans le get: "
                return self._position
        
            @position.setter
            def position(self, deplacement):
                print "dans le set: "
                self._position = deplacement + self._position
        • Partager sur Facebook
        • Partager sur Twitter
          3 février 2013 à 12:27:12

          @spider-mario : pas dans la version de Python qu'il utilise (2.5).

          Documentation Python - property() :

          New in version 2.2.

          Changed in version 2.5: Use fget‘s docstring if no doc given.

          Changed in version 2.6: The getter, setter, and deleter attributes were added.

          • Partager sur Facebook
          • Partager sur Twitter
          Zeste de Savoir, le site qui en a dans le citron !
            3 février 2013 à 14:58:27

            Salut!

            J'ai cherche ici l'implementation de la classe property , ne comprenant pas comment se faisait -il qu'on puisse se servir de property comme decorateur. Il semble cependant que n'y figurent pas les methodes getter et setter qui etaient censees me permettre d'y voir plus clair. J'ai donc funestement echoue dans ma quete de savoir. Quelque valeureux programmeur pourrait t'il m'eclairer de sa lanterne, histoire que j'y voie un peu plus clair? Merci!

            -
            Edité par stackOverflow 3 février 2013 à 15:41:44

            • Partager sur Facebook
            • Partager sur Twitter
              3 février 2013 à 15:20:23

              Mieux que ça encore : j'en avais fait l'objet d'un exercice très détaillé sur ce forum il y a un peu plus d'un an. Ça t'aidera sûrement à en comprendre les subtilités.

              -
              Edité par nohar 3 février 2013 à 15:20:55

              • Partager sur Facebook
              • Partager sur Twitter
              Zeste de Savoir, le site qui en a dans le citron !
                3 février 2013 à 15:59:55

                @nohar: J'ai lu le TP en question, et j'ai repondu a toutes les questions sauf la derniere sans trop de problemes (il faut dire que c'etait un peu de la triche vu que j'avais lu la doc juste avant...) mais je suis toujours bloque au meme point: les decorateurs. Et j'ai pas trouve de solution a cette partie dans le forum.

                PS: voila mon code

                class prop:
                    def __init__(self, fget=None, fset=None, fdel=None, fdoc=None):
                        self._fget = fget
                        self._fset = fset
                        self._fdel = fdel
                        self.__doc__ = fdoc
                
                    def __get__(self, instance, owner):
                        if self._fget == None:
                            raise (AttributeError, "can't get the attribute")
                        return self._fget(instance)
                        
                    def __set__(self, instance, value):
                        if self._fset == None:
                            raise (AttributeError, "can't set the attribute")
                        self._fset(instance, value)
                
                    def __del__(self):
                        self._fdel()

                -
                Edité par stackOverflow 3 février 2013 à 18:48:43

                • Partager sur Facebook
                • Partager sur Twitter
                  3 février 2013 à 18:15:14

                  Tu peux trouver la solution dans ce post de yoch. C'est tout bête en réalité (le "Attention c'est difficile…" dans l'énoncé est ironique) :

                  class prop:
                      def __init__(self, fget=None, fset=None, fdel=None):
                          self.__fget = fget
                          self.__fset = fset
                          self.__fdel = fdel
                  
                      def __get__(self, instance, owner):
                          if self.__fget is None:
                              raise AttributeError
                          return self.__fget(instance)
                  
                      def __set__(self, instance, value):
                          if self.__fset is None:
                              raise AttributeError
                          return self.__fset(instance, value)
                  
                      def __delete__(self, instance):
                          if self.__fdel is None:
                              raise AttributeError
                          self.__fdel(instance)
                  
                      def setter(self, fn):
                          self.__fset = fn
                          return self
                  
                      def deletter(self, fdel):
                          self.__fdel = fdel
                          return self
                  

                  Ceci :

                  @width.setter
                  def width(self, val):
                      self.__width = val
                  

                  Est syntaxiquement équivalent à cela :

                  def set_width(self, val):
                      self.__width = val
                  
                  width = width.setter(set_width)
                  

                  Il suffit donc que la property ait une méthode setter qui accepte une fonction en argument. :)

                  -
                  Edité par nohar 12 février 2013 à 1:48:14

                  • Partager sur Facebook
                  • Partager sur Twitter
                  Zeste de Savoir, le site qui en a dans le citron !
                    3 février 2013 à 19:35:33

                    Merci nohar! Tout est clair maintenant grace a toi (enfin il me semble, vu que Python n'a jamais fini de me surprendre).

                    Seule une derniere petite question demeure: quelle est l'utilite de retourner self dans le getter et le setter?

                    Sinon, le TP sur les metaclasses a t-il eu lieu pour finir (j'ai cherche sans resultats)? Parce-que je suis tres interresse.

                    -
                    Edité par stackOverflow 3 février 2013 à 19:56:44

                    • Partager sur Facebook
                    • Partager sur Twitter
                      3 février 2013 à 20:02:49

                      En fait c'est un poil plus compliqué. C'est lié à la nature des décorateurs. Je me suis un peu gouré dans mon precedent post. J'ai édité.

                      PS : Non, je n'ai finalement pas fait le tp sur les metaclasses. Je l'ai zappé avec le temps et le manque de dispo : c'aurait été trop long à préparer pour trop peu de gens.

                      -
                      Edité par nohar 3 février 2013 à 20:16:43

                      • Partager sur Facebook
                      • Partager sur Twitter
                      Zeste de Savoir, le site qui en a dans le citron !
                        3 février 2013 à 20:33:31

                        Maintenant j'ai bien compris. Merci!
                        • Partager sur Facebook
                        • Partager sur Twitter
                          4 février 2013 à 9:52:44

                          Merci à vous 2 pour votre aide

                          Effectivement en faisant dériver ma classe de object, j'arrive à utiliser les propriétés.

                          J'avais vu la page de documentation citée par Nohar mais je l'avais mal comprise :honte:

                          • Partager sur Facebook
                          • Partager sur Twitter
                            10 février 2013 à 22:10:01

                            @nohar: En manipulant les descripteurs, je me suis rendu compte que lorsque la fonction getter et la fonction setter n'ont pas le meme nom, une AttributeError est levee quand je me sers de property.

                            Le code:

                            class Plateau(object):
                                def __init__(self):
                                    self._position = 0
                             
                                @property
                                def position(self):
                                    print "dans le get: "
                                    return self._position
                             
                                @position.setter
                                def autreNom(self, deplacement):
                                    print "dans le set: "
                                    self._position = deplacement + self._position

                            donne une AttributeError: can't set attribute a l'assignement de position, ce qui n'est pas le cas lorsque je me sers de la classe codee par yoch. Quelle est la difference d'implementation entre celle de yoch et l'"officielle"?

                            -
                            Edité par stackOverflow 10 février 2013 à 22:15:13

                            • Partager sur Facebook
                            • Partager sur Twitter
                              11 février 2013 à 9:20:48

                              La dernière fois que j'ai regardé, les properties étaient une builtin implémentée en C. J'ai pas trop le temps de mettre à jour le repo pour vérifier, mais tu peux toujours y jeter un oeil toi même.

                              Il est possible que le décorateur @property ne surcharge pas sa méthode set par défaut, et que l'appel a @truc.setter recrée une property complète qui enveloppe la première.

                              • Partager sur Facebook
                              • Partager sur Twitter
                              Zeste de Savoir, le site qui en a dans le citron !
                                11 février 2013 à 11:32:35

                                Pour le repository, c'est de ca que tu veux parler?

                                Si oui. comment j'accedes au code en rapport avec les properties apres?

                                Merci!

                                -
                                Edité par stackOverflow 11 février 2013 à 11:33:37

                                • Partager sur Facebook
                                • Partager sur Twitter
                                  11 février 2013 à 11:42:10

                                  grep -Rin "property" . à la racine ?

                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                  Zeste de Savoir, le site qui en a dans le citron !
                                    11 février 2013 à 12:56:03

                                    J'ai telecharge le code source d'ici directement en zip plutot qu'avec Mercurial (je sais pas si j'ai bien fait...).

                                    Ensuite j'ai fait legrepet la evidemment j'ai en output tous les fichiers qui contiennent le mot property, et il va sans dire que je ne m'y retrouves pas du tout. J'ai surement loupe quelque chose...

                                    Merci de ta patience!

                                    -
                                    Edité par stackOverflow 11 février 2013 à 12:57:22

                                    • Partager sur Facebook
                                    • Partager sur Twitter
                                      11 février 2013 à 14:59:08

                                      Actuellement (sur la branche default du repository, donc en gros dans le code de CPython 3.4), on trouve ceci dans Objects/descrobject.c (ligne 1543) :

                                      PyTypeObject PyProperty_Type = {
                                          /* ... */
                                          0,                                          /* tp_iternext */
                                          property_methods,                           /* tp_methods */
                                          property_members,                           /* tp_members */
                                          property_getsetlist,                        /* tp_getset */
                                          0,                                          /* tp_base */
                                          /* ... */
                                      };
                                      

                                      Si on cherche property_methods on tombe là-dessus (vers la ligne 1280) :

                                      PyDoc_STRVAR(getter_doc,
                                                   "Descriptor to change the getter on a property.");
                                      
                                      static PyObject *
                                      property_getter(PyObject *self, PyObject *getter)
                                      {
                                          return property_copy(self, getter, NULL, NULL);
                                      }
                                      
                                      
                                      PyDoc_STRVAR(setter_doc,
                                                   "Descriptor to change the setter on a property.");
                                      
                                      static PyObject *
                                      property_setter(PyObject *self, PyObject *setter)
                                      {
                                          return property_copy(self, NULL, setter, NULL);
                                      }
                                      
                                      
                                      PyDoc_STRVAR(deleter_doc,
                                                   "Descriptor to change the deleter on a property.");
                                      
                                      static PyObject *
                                      property_deleter(PyObject *self, PyObject *deleter)
                                      {
                                          return property_copy(self, NULL, NULL, deleter);
                                      }
                                      
                                      
                                      static PyMethodDef property_methods[] = {
                                          {"getter", property_getter, METH_O, getter_doc},
                                          {"setter", property_setter, METH_O, setter_doc},
                                          {"deleter", property_deleter, METH_O, deleter_doc},
                                          {0}
                                      };
                                      

                                      Et la fonction property_copy, qui n'est ni plus ni moins qu'un constructeur par recopie (ligne 1375) :

                                      static PyObject *
                                      property_copy(PyObject *old, PyObject *get, PyObject *set, PyObject *del)
                                      {
                                          propertyobject *pold = (propertyobject *)old;
                                          PyObject *new, *type, *doc;
                                      
                                          type = PyObject_Type(old);
                                          if (type == NULL)
                                              return NULL;
                                      
                                          if (get == NULL || get == Py_None) {
                                              Py_XDECREF(get);
                                              get = pold->prop_get ? pold->prop_get : Py_None;
                                          }
                                          if (set == NULL || set == Py_None) {
                                              Py_XDECREF(set);
                                              set = pold->prop_set ? pold->prop_set : Py_None;
                                          }
                                          if (del == NULL || del == Py_None) {
                                              Py_XDECREF(del);
                                              del = pold->prop_del ? pold->prop_del : Py_None;
                                          }
                                          if (pold->getter_doc && get != Py_None) {
                                              /* make _init use __doc__ from getter */
                                              doc = Py_None;
                                          }
                                          else {
                                              doc = pold->prop_doc ? pold->prop_doc : Py_None;
                                          }
                                      
                                          new =  PyObject_CallFunction(type, "OOOO", get, set, del, doc);
                                          Py_DECREF(type);
                                          if (new == NULL)
                                              return NULL;
                                          return new;
                                      }
                                      

                                      En gros, la différence est bien celle que je pressentais : lorsque tu appelles @truc.setter dans ton code en Python, la méthode va créer une nouvelle property, qui correspond à la property truc augmentée d'un setter, alors que dans l'implémentation pur python que l'on faisait dans le TP, on se contentait de rajouter un attribut à la property déjà existante.

                                      Comme dans la builtin, le setter n'est pas contenu dans la propriété créée avec le décorateur @property, tu es obligé de donner le même nom que la propriété à la méthode qui fait office de setter, puisque c'est le nom sous lequel va être stocké la nouvelle property (celle augmentée du setter). Limitation que n'a pas l'implémentation pur Python (dans l'implémentation pur Python, sur le dernier exemple que tu as donné, Plateau.position et Plateau.autreNom vont être deux références vers la même property).

                                      PS : Je suis vraiment pas sûr d'être clair…

                                      Puisque tout le monde n'est pas forcément à l'aise en C ou bien avec l'API de CPython, voilà le code Python équivalent :

                                      class property:
                                          def __init__(self, fget=None, fset=None, fdel=None):
                                              self.__fget = fget
                                              self.__fset = fset
                                              self.__fdel = fdel
                                              self.__doc__ = fget.__doc__
                                       
                                          def __get__(self, instance, owner):
                                              if self.__fget is None:
                                                  raise AttributeError
                                              return self.__fget(instance)
                                       
                                          def __set__(self, instance, value):
                                              if self.__fset is None:
                                                  raise AttributeError
                                              return self.__fset(instance, value)
                                       
                                          def __delete__(self, instance):
                                              if self.__fdel is None:
                                                  raise AttributeError
                                              self.__fdel(instance)
                                       
                                          def getter(self, fget):
                                              return property(fget, self.__fset, self.__fdel)
                                      
                                          def setter(self, fset):
                                              return property(self.__fget, fset, self.__fdel)
                                       
                                          def deleter(self, fdel):
                                              return property(self.__fget, self.__fset, fdel)
                                      

                                      -
                                      Edité par nohar 12 février 2013 à 2:12:24

                                      • Partager sur Facebook
                                      • Partager sur Twitter
                                      Zeste de Savoir, le site qui en a dans le citron !
                                        11 février 2013 à 16:05:11

                                        nohar a écrit:

                                        PS : Je suis vraiment pas sûr d'être clair…

                                        Non ca l'est parfaitement.

                                        Encore merci!

                                        • Partager sur Facebook
                                        • Partager sur Twitter

                                        utilisation des propriétés

                                        × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                        × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                                        • Editeur
                                        • Markdown