Partage
  • Partager sur Facebook
  • Partager sur Twitter

Passage de paramètres

décorateur

Sujet résolu
    1 mars 2013 à 23:20:47

    Bonsoir,

    En cours d'apprentissage de Python je m'expérimente un peu sur les décorateurs de fonctions.

    code :

    def stable (value):
    	return lambda *args, **kargs: value
    
    def once_per_object (unstable):
    	"""Method decorator. Run only once 'unstable' per instance"""
    	def stabilize (*args, **kargs):
    		self = args [0]
    		result = unstable (*args)
    		setattr (self, unstable.__name__, stable (result))
    		return result
    	return stabilize
    
    def once (unstable):
    	"""Method decorator. Run only once 'unstable'."""
    	def stabilize (*args, **kargs):
    		self = args [0]
    		result = unstable (*args)
    		setattr (self.__class__, unstable.__name__, stable (result))
    		return result
    	return stabilize


    Dans un sujet précédent (quelques-uns s'en souviendront), j'ai pu apprendre que l'utilisation des paramètres optionnels est une pratique courante en Python. J'aimerais donc que mon décorateur les supporte. Pour se faire je dois appeler 'unstable ' en lui passant les paramètres sans label et les paramètres avec label.

    C'est à ce niveau que je bloque. En effet la syntaxe suivante ne donne pas l'effet estompé :

    unstable (*args, **kargs)

    Merci

    -
    Edité par Conaclos 1 mars 2013 à 23:36:16

    • Partager sur Facebook
    • Partager sur Twitter
      1 mars 2013 à 23:53:38

      J'ai un peu de mal à comprendre ce que tu essayes de faire avec ces décorateurs.

      Un petit exemple d'application ?

      • Partager sur Facebook
      • Partager sur Twitter
      Zeste de Savoir, le site qui en a dans le citron !
        2 mars 2013 à 0:41:02

        Le deuxième décorateur ('once') permet de créer des constantes complexes, j'entend par complexe avec de nombreuses étapes d'initialisation. Un autre avantage est que leur évaluation est paresseuse.
        Il peut également servir pour initialiser des points particulier de l'application et remplacer les singletons.

        Le premier décorateur ('once_per_object) a plusieurs utilités :
        - attribut transitif constant (constant pour un objet).
        initialisation paresseuse.

        Le but de l’initialisation paresseuse est de permettre un gain en mémoire (ça reste à vérifier).

        class A:
        	x = []
        
        	def has (self, v):
        		return v in self.x
        	
        	@once_per_object
        	def create_x (self):
        		self.x = []
        	
        	def add (self, v):
        		self.create_x ()
        		self.x.append (v)

        -
        Edité par Conaclos 2 mars 2013 à 11:35:43

        • Partager sur Facebook
        • Partager sur Twitter
          2 mars 2013 à 6:56:58

          Ca a l'air de marcher correctement...

          # -*- coding: utf-8 -*-
          import unittest
          
          
          def stable(value):
              return lambda *args, **kwargs: value
          
          def once_per_object(unstable):
              """Method decorator. Run only once 'unstable' per instance"""
              def stabilize(*args, **kwargs):
                  self = args[0]
                  result = unstable(*args, **kwargs)
                  setattr(self, unstable.__name__, stable(result))
                  return result
              return stabilize
          
          def once(unstable):
              """Method decorator. Run only once 'unstable'."""
              def stabilize(*args, **kwargs):
                  self = args[0]
                  result = unstable(*args, **kwargs)
                  setattr(self.__class__, unstable.__name__, stable(result))
                  return result
              return stabilize
          
          
          class A(object):
              x = []
          
              def has(self, v):
                  return v in self.x
          
              @once_per_object
              def create_x(self):
                  self._x = []
          
              def add(self, v):
                  self.create_x()
                  self.x.append(v)
          
          class Test(unittest.TestCase):
              def test_a(self):
                  a = A()
                  self.assertFalse(hasattr(a, '_x'))
                  a.add(1)
                  self.assertTrue(hasattr(a, '_x'))
                  l1 = a._x
                  a.add(2)
                  self.assertEqual(A.x, [1, 2]) # Note que je ne comprends pas ton exemple
                  self.assertIs(l1, a._x) # _x est toujours le même objet
          
              def test_object(self):
                  calls = []
                  class C(object):
                      @once_per_object
                      def get_list(self, pos, kw):
                          calls.append((pos, kw))
                          return list(xrange(pos, kw))
                      def foo(self):
                          return self.get_list(2, kw=5)
                      def bar(self):
                          return self.get_list(3, kw=4)
                  c = C()
                  l1 = c.foo()
                  l2 = c.bar()
                  self.assertIs(l1, l2) # Le même objet
                  self.assertEqual(calls, [(2, 5)]) # Un seul appel
          
              def test_static(self):
                  calls = []
                  class C(object):
                      @once
                      def foo(self, arg1, arg2, kw1, kw2):
                          calls.append((arg1, arg2, kw1, kw2))
                          return object()
                  c = C()
                  self.assertIs(c.foo(1, 2, kw1=3, kw2=4), c.foo(7, 3, kw1=6, kw2=2))
                  self.assertEqual(calls, [(1, 2, 3, 4)])
          
          
          if __name__ == '__main__':
              unittest.main()
          • Partager sur Facebook
          • Partager sur Twitter
            2 mars 2013 à 11:56:49

            Ah il devait être tard, la fatigue ...

            Je n'arrive pas à reproduire l'erreur que j'obtenais, je me suis certainement trompé quelques part d'autre car ça marche parfaitement ...

            Désolé du dérangement ...

            Je me suis également trompé dans l'exemple (que je viens de corriger). La commande 'create_x' à un effet sur 'x' et non sur '_x'. Sinon ça n'a aucun intérêt ...

            En fait lorsque tu créé une instance de 'A' tu obtiens un objet sans attribut. 'x' est une constante (immuable). Tu peux ainsi appeler des méthodes sans effet de bord (requête) sur 'x'.

            o = A ();
            o.has (2)
            o.has (4)
            # ...

            Donc tes requêtes fonctionne sur 'x' sans pour autant avoir créer un 'x' dans chaque objet de type 'A'.

            La création de l'attribut est repoussée lors de l'utilisation de méthode à effet de bord sur 'x'. Par exemple lorsque tu appelle 'add'. On aurait pu très bien utiliser une condition mais il aurait été préférable de l'inclure dans une fonction (si il existe plusieurs méthode à effet de bord pour 'x'). De plus on perd le temps de l'évaluation d'une condition pas forcément clair :

            def create_x (self):
                if self.x == A.x:
                    self.x = []

            C'est à ce moment qu'entre en jeu les routines à exécution unique par objet :

            @once_per_object
            def create_x (self):
                self.x = []

            ça me semble plus simple et plus efficient.

            Ce chargement paresseux avec constante est un pattern que j'ai développé dans ma bibliothèque javascript orienté objet. Je n'ai pas la preuve qu'elle apporte quelque chose en terme de mémoire ou de performance globale.

            A noter que je l'utilise dans des conditions déterminées :
            - utilisation importante par l'attribut de mémoire ou de temps d'initialisation
            - requête possible sur un objet par défaut
            - commandes probablement peu utilisées, notamment après la création de l'instance

            le choix reste parfois subjectif car on ne peut pas prévoir l'utilisation "normale" d'un objet, à supposer qu'il y en ai une.

            A = Type.a_new ();
            A.effect ("A");
            
            A.define ({
                x: [],
            
                request: function () {
                    // use request on 'x'
                },
            
                create_x: function () {
                    this.x = [];
                },
            
                command: function () {
                    this.create_x ();
                    // set 'x'
                }
            });
            
            A.run_once ("create_x");

            -
            Edité par Conaclos 1 mai 2014 à 11:57:39

            • Partager sur Facebook
            • Partager sur Twitter

            Passage de paramètres

            × 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