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 :

membres de classe private/protected (yet another try)


Sujet :

Python

  1. #1
    Candidat au Club
    Homme Profil pro
    Directeur technique
    Inscrit en
    Août 2012
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Directeur technique
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Août 2012
    Messages : 6
    Points : 2
    Points
    2
    Par défaut membres de classe private/protected (yet another try)
    Salutations,

    long time no see (oui oui je vais éviter l'anglais ;p), à vrai dire depuis les concours c2i diggers, ça date, salut Nix et … wazaaaaa !

    Pour des raisons que je suis prêt à défendre même si ça n'est pas le sujet, j'essaye de mettre en place un mécanisme "fort" de gestion des membres de classes public/protected/private. Fort mais simple, l'idée étant que ça doit rester léger dans l'utilisation …

    Après de nombreuses recherches et essais ratés, j'y suis presque, mais ça coince

    les contraintes :
    - doit passer sur toutes les versions de python à partir de la 2.5, sans tests bourrins sur du sys.version_info dans les modules (2.5, 2.6 et 2.7 en activité, 3.x à l'horizon)
    - le plus léger possible dans la syntaxe pour ne pas polluer la définition des classes
    - toujours pour ne pas polluer, pas de brouzouf au niveau des locales de la classe, tout doit se passer dans les accesseurs
    - fonctionnement classique : public sans restriction, private réservé à la classe, protected accessible aux sous-classes
    - support complet (get/set/delattr)
    - respect des conventions python en cas d'inspection (simple / double underscore)

    Mon approche actuelle se base sur les décorateurs qui sont en partie viables dès la 2.5 tant qu'on reste dans une utilisation simple (pas de @property / @prop.setter/deleter, pas de surcharge sale des __setattr__ __getattr__ non plus à l'inverse). Le but étant de me retrouver avec des définition de membres de ce type :

    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
     
    from MClasses import public, protected, private
     
    class MyClass(object):
    	def __init__(self):
    		self._pub    = 42
    		self._prot   = 42
    		self.__priv  = 42
     
            @public
    	def pub(): pass
     
            @protected
    	def prot(): pass
     
            @private
    	def priv(): pass
     
    	def assign(self):
    		self.pub  = 666
    		self.prot  = 666
    		self.priv  = 666
     
    class MySubClass(MyClass):
    	pass
    Dans ce contexte, un myclass.assign() doit marcher de bout en bout, un mysubclass.assign() doit lever une exception sur le self.priv, et une affectation de l'extérieur doit péter sur prot et priv.

    Je coince pour l'instant à la récupération de la classe d'origine d'un membre dans les decorateurs, étant donné que la classe n'existe justement pas lors du chargement de la définition … j'ai tenté de créer une classe vide juste avant la "vraie" définition (class MyClass(object): pass) pour pouvoir la passer en argument (genre @public(MyClass)), mais l'id diffère (self.__class__ != MyClass), et la recherche d'héritage par issubclass/isinstance échoue, logique.

    Quelques bouts de mon implémentation super naïve :

    get_call_class.py, pour récupérer la classe de l'objet qui accède à un membre :
    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
     
    import inspect
     
    from   get_frame_class import get_frame_class
     
    def get_call_class(depth=1):
        """class get_call_class(int depth=1)                                                                              
                                                                                                                          
        Returns the current calling class at given depth (1 by default so that we get                                     
        the first container available)."""
     
        # add 2 depth to avoid get_call_class and get_frame_class levels                                                  
        depth += 2
     
        stack = inspect.stack()
        if stack and len(stack) > depth and stack[depth]:
            return get_frame_class(stack[depth][0])
     
        return None
    get_frame_class.py, pour extraire la classe à partir d'une frame :
    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
     
    import inspect
     
    def get_frame_class(obj):
        """class get_frame_class(frame obj)                                                                               
                                                                                                                          
        Returns the class object for the given frame (see inspect module)."""
     
        args, _, _, value_dict = inspect.getargvalues(obj)
        if len(args) and args[0] == 'self':
            instance = value_dict.get('self', None)
            if instance:
                return getattr(instance, '__class__', None)
     
        return None
    get_func_class.py pour avoir la classe de l'objet qui a défini un membre / méthode (c'est là que ça bloque ;p)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    import inspect
     
    def get_func_class(func):
      if hasattr(func, "im_class"):
        for cls in inspect.getmro(func.im_class):
          if func.__name__ in cls.__dict__:
            return cls
     
      return None
    safe_decorator.py, un décorateur central appelé par les décorateurs public/private/protected lors de la construction, merci stack overflow !
    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
     
    from get_func_class import get_func_class
    from safe_getattr   import safe_getattr
    from safe_setattr   import safe_setattr
    from safe_delattr   import safe_delattr
     
    def safe_decorator(func, scope):
        ops = func() or {}
        name = ops.get("prefix", "__" if scope == "private" else "_") + func.__name__
     
        cls = get_func_class(func)
     
        fget = ops.get("fget", lambda self: safe_getattr(self, name, scope, cls))
        fset = ops.get("fset", lambda self, value: safe_setattr(self, name, value, scope, cls))
        fdel = ops.get("fdel", lambda self: safe_delattr(self, name, scope, cls))
     
        return property(fget, fset, fdel, ops.get("doc", ""))
    public.py, private.py, protected.py, les surcharges de safe_decorator, passant juste un argument "scope" pour préciser dans quel cas on se trouve :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    def public(func):                                                                                                   
        return safe_decorator(func, "public")
     
    def private(func):                                                                                                   
        return safe_decorator(func, "private")
     
    def protected(func):                                                                                                   
        return safe_decorator(func, "protected")
    safe_setattr.py, le setter générique (laissons de côté les safe_getattr et safe_delattr pour l'instant, il suffit de les remplacer par de simples getattr/delattr)
    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
     
    from get_call_class import get_call_class
     
    def safe_setattr(self, name, value, scope, cls):
        caller = get_call_class()
        print "SETATTR " + name + " " + str(cls) + " " + str(caller)
     
        doit = True
        if scope == "private":
            doit = caller == cls
        elif scope == "protected":
            doit = cls and issubclass(caller, cls)
     
        if doit:
            setattr(self, name, value)
        else:
    	raise AttributeError(str(cls) + "." + name + " is " + scope + " !")
    La question principale reste donc : comment récupérer la classe de définition d'un membre dans un décorateur, ou en tout cas un élément de comparaison avec la classe appelante ? Toute astuce sera la bienvenue !

    La question subsidiaire : l'approche vous semble viable ? réalisable ? pythonesque ? ^^

    Quoi qu'il en soit merci aux courageu(x|ses) qui auront eu le courage de lire cet horrible pavé jusqu'au bout !

    Tonio

  2. #2
    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,

    functools.wraps (ou @abc.abstractmethod mais >2.6) ?

    Mais bon... Tout cela me semble bien loin du 'programmeur responsable' Python.

    @+

  3. #3
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 484
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 484
    Points : 9 286
    Points
    9 286
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    Le mécanisme intégré à Python pour rendre une méthode "privée" est très simple: il suffit de préfixer son nom avec 2 blancs soulignés ('__'):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Test(object):
     
        def fonctionpublique(self):
            pass
     
        def __fonctionprivee(self):
            pass
     
    a = Test()
    a.fonctionpublique()
    a.__fonctionprivee() # => exception: AttributeError: 'Test' object has no attribute '__fonctionprivee'
    Toute tentative d'appel externe à la méthode "__fonctionprivee" déclenche une exception: "AttributeError: 'Test' object has no attribute '__fonctionprivee' "

    C'est vrai aussi pour les variables d'instance.

    Sans vouloir te freiner dans ton élan: tu es sûr que ça ne suffit pas?

  4. #4
    Membre expérimenté
    Profil pro
    Développeur en systèmes embarqués retraité
    Inscrit en
    Mars 2006
    Messages
    952
    Détails du profil
    Informations personnelles :
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Mars 2006
    Messages : 952
    Points : 1 351
    Points
    1 351
    Par défaut
    Salut,
    Citation Envoyé par tyrtamos Voir le message
    Le mécanisme intégré à Python pour rendre une méthode "privée" est très simple: il suffit de préfixer son nom avec 2 blancs soulignés ('__'): .../... C'est vrai aussi pour les variables d'instance.
    C'est ça que j'aime bien sur ce forum... Tous les jours, j'apprends!

    A+

    Pfeuh

  5. #5
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 440
    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 440
    Points : 37 032
    Points
    37 032
    Par défaut
    Salut,
    Joli chantier!
    Je coince pour l'instant à la récupération de la classe d'origine d'un membre dans les decorateurs, étant donné que la classe n'existe justement pas lors du chargement de la définition … j'ai tenté de créer une classe vide juste avant la "vraie" définition (class MyClass(object): pass) pour pouvoir la passer en argument (genre @public(MyClass)), mais l'id diffère (self.__class__ != MyClass), et la recherche d'héritage par issubclass/isinstance échoue, logique.
    Si vous faites abstraction de la possibilité de modifier dynamiquement une classe, i.e. en restant "statique", passer par les "metaclass" pourrait être une possibilité (à explorer): les méthodes __new__ et __init__ étant appelées "avant" l'initialisation des différents attributs de la classe.

    - W

  6. #6
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 485
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 485
    Points : 13 695
    Points
    13 695
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par pfeuh Voir le message
    Salut,
    C'est ça que j'aime bien sur ce forum... Tous les jours, j'apprends!
    A+
    Pfeuh
    Ce mécanisme est connu sous le nom de name mangling. Voir la partie 9.6 sur les classes : http://docs.python.org/tutorial/classes.html ^^

  7. #7
    Membre expérimenté
    Profil pro
    Développeur en systèmes embarqués retraité
    Inscrit en
    Mars 2006
    Messages
    952
    Détails du profil
    Informations personnelles :
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Mars 2006
    Messages : 952
    Points : 1 351
    Points
    1 351
    Par défaut
    Citation Envoyé par Bktero Voir le message
    Ce mécanisme est connu sous le nom de name mangling. Voir la partie 9.6 sur les classes : http://docs.python.org/tutorial/classes.html ^^
    Génial, y'a même une ancre sur le titre du chapitre, du coup on peut donner le lien direct:
    9.6. Private Variables and Class-local References

    A+

    Pfeuh

  8. #8
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 485
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 485
    Points : 13 695
    Points
    13 695
    Billets dans le blog
    1
    Par défaut
    Oh trop bien ! J'avais essayé de faire un lien direct, mais pas de possibilité de clic sur le titre. J'aurais du essayé le petit logo à droite !

  9. #9
    Candidat au Club
    Homme Profil pro
    Directeur technique
    Inscrit en
    Août 2012
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Directeur technique
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Août 2012
    Messages : 6
    Points : 2
    Points
    2
    Par défaut
    Bonjour, merci pour vos réponses, ça ouvre des perspectives !

    @Patrice

    L'approche par wraps ne me plait pas trop, ça impose un "wrapper de wrapper" qui si je me souviens bien sera exécuté à chaque accès, et pas seulement en substitution à la déclaration de la classe. Pour l'instant c'est un bouzin pas optimisé du tout, mais j'aimerais éviter à terme de plomber les perfs de mes classes

    Je ne suis pas certain de comprendre la notion de "programmeur responsable", est-ce si déplacé de vouloir mettre en place une usine pareille ?

    @tyrtamos

    En effet le mécanisme class.__priv -> class._classname__priv est la méthode tacite pour cacher un membre, seulement :
    - ça ne permet pas de prendre la main sur les accès à ces membres, déterminer le comportement lorsqu'on tape dessus (par exemple surcharger par un @quiet pour stopper les raise).
    - ça ne gère pas le cas "protected"
    - l'AttributeError qui découle d'un accès est logique mais inadapté, comment faire la différence avec un "vrai" cas d'attribut absent ?

    En fait ça va surtout concerner des objets "business", processés par pas mal de scripts auto, plugins maison etc., et ça me permettra donc d'intégrer ça dans les tests unitaires, vérifier régulièrement que tout roule, faire des analyses d'impact etc. C'est beaucoup plus pour de la protection "métier" que pour locker le code.

    Mais je suis d'accord, dans l'immédiat il suffirait d'oublier @public (supposé ne rien faire) et @private (trivial)

    @wiztricks

    C'est effectivement du chantier détente bien sale, il fait trop chaud pour coder "sérieux" en ce moment ;p

    L'approche est super intéressante, et viable depuis la 2.2, ça se tente. J'essaye de bricoler un update des wrappers dans le __init__ pour l'instant, pas très concluant, malgré le remplacement des property avant l'instanciation je ne retrouve pas encore mes petits, mais c'est prometteur. Le principe reste léger et limité à la déclaration des classes … sur le papier ça le fait, merci pour la piste !

    Tonio

  10. #10
    Expert éminent
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    3 939
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 3 939
    Points : 7 348
    Points
    7 348
    Par défaut
    Je vous laisse lire ce post, la méthode "protected" est faisable en python.

    Ce que vous demandez ressemble beaucoup à de la modification dynamique de classe et je rejoins la proposition de Wiztricks.

    Mais je rejoins l'avis de Patrice, pensez python et non C++ ou java.

  11. #11
    Membre éprouvé
    Inscrit en
    Août 2010
    Messages
    1 124
    Détails du profil
    Informations forums :
    Inscription : Août 2010
    Messages : 1 124
    Points : 1 277
    Points
    1 277
    Par défaut
    Bonjour,

    comment récupérer la classe de définition d'un membre dans un décorateur
    -> passer par les "metaclass"
    -> créer la classe, puis la décorer après. Par exemple
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    # a le mérite d'isoler le code gérant les accès après la déf. de la classe
    class A:
        def foo(self):pass
    A.foo= decorator(A.foo,A)
    Sinon, plutôt que de devoir définir une méthode décorée par attributs (je trouve la syntaxe lourde), pourquoi ne pas préciser globalement les accès en décorant la classe, par exemple
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    @access(private=('attribut1','method1'), protected=('attribut2','__init__'))
    class A: pass
    Même si cela ne suffit pas pour définir/modifier dynamiquement des attributs protégés ; cela rend la déf de la classe plus légère si tu sais à l'avance quels attributs sont protégés.

  12. #12
    Candidat au Club
    Homme Profil pro
    Directeur technique
    Inscrit en
    Août 2012
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Directeur technique
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Août 2012
    Messages : 6
    Points : 2
    Points
    2
    Par défaut
    Citation Envoyé par fred1599 Voir le message
    Je vous laisse lire ce post, la méthode "protected" est faisable en python.
    Malheureusement, je tente de mettre en place une méthode valable à partir de python 2.5, sans détection de version. Du coup le passage par un @property.setter ne fonctionnera pas !

    Citation Envoyé par fred1599 Voir le message
    Mais je rejoins l'avis de Patrice, pensez python et non C++ ou java.
    Les vieilles recettes ont la peau dure, quand on est habitué à certains mécanismes, mais ça m'a sauvé la mise tellement souvent ! Le python n'est pas le seul langage impliqué dans notre pipeline, du coup j'essaye de maintenir des logiques communes de l'un à l'autre, ne serait-ce que pour éviter les noeuds au cerveau ...

    Je crois me souvenir d'un vieux débat PEP sur la pertinence de la gestion d'exception à outrance qui s'était soldée par la conclusion qu'il faut avoir un peu plus confiance : en soi même, en son code, et en ceux qui interviennent dessus

    Citation Envoyé par VV33D Voir le message
    -> créer la classe, puis la décorer après.
    rah exactement l'inverse de ma minable tentative de :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    class A: pass
    class A:
        @decorator(A)
        def foo(self): pass
    et moins de pollution visuelle dans la définition de la classe, ça sonne bien.

    Citation Envoyé par VV33D Voir le message
    Sinon, plutôt que de devoir définir une méthode décorée par attributs (je trouve la syntaxe lourde), pourquoi ne pas préciser globalement les accès en décorant la classe, par exemple
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    @access(private= ('attribut1','method1'), protected=('attribut2','__init__')
    class A: pass
    Je suis fan de la notation ! En effet, beaucoup plus léger, ouvert à la protection des méthodes si besoin, parfaitement clair à la lecture du code ... bon, il faut tout recommencer

    Tonio

  13. #13
    Expert éminent
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    3 939
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 3 939
    Points : 7 348
    Points
    7 348
    Par défaut
    Malheureusement, je tente de mettre en place une méthode valable à partir de python 2.5, sans détection de version. Du coup le passage par un @property.setter ne fonctionnera pas !
    à moins d'avoir une très bonne raison (utilisation de pymedia par exemple), la version 2 tend à disparaître.

  14. #14
    Membre éprouvé
    Inscrit en
    Août 2010
    Messages
    1 124
    Détails du profil
    Informations forums :
    Inscription : Août 2010
    Messages : 1 124
    Points : 1 277
    Points
    1 277
    Par défaut
    bon, il faut tout recommencer
    Ravi que mes idées te plaisent

    par contre, avec la syntaxe "classe décorée", il reste un problème à gérer: comment rajouter dynamiquement des attributs protégés (ie. après la création de la classe ou de l'instance).

    Souhaites tu une telle fonctionnalité ? Cela aurait il du sens de définir les accès relativement à l'instance, et non à la classe ? Par exemple, 2 instances différentes gérant les accès différemment, que ce soit pour les attributs ou les méthodes.

  15. #15
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 440
    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 440
    Points : 37 032
    Points
    37 032
    Par défaut
    Citation Envoyé par T00N3 Voir le message
    Je ne suis pas certain de comprendre la notion de "programmeur responsable", est-ce si déplacé de vouloir mettre en place une usine pareille ?
    Python étant interprété quel que soit le mécanisme de protection que vous pourrez construire, le programmeur pourra toujours passer à côté.
    Si vous devez avoir des protections fortes, vous devrez utiliser un langage de programmation "compilé" qui réalise des mécanismes de protection prouvés.
    Entre les deux, restent les conventions usuelles... elles pourront être suivies ou pas mais elles ont le mérite d'exister et de ne pas coûter grand chose.

    - W

  16. #16
    Candidat au Club
    Homme Profil pro
    Directeur technique
    Inscrit en
    Août 2012
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Directeur technique
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Août 2012
    Messages : 6
    Points : 2
    Points
    2
    Par défaut
    Citation Envoyé par fred1599 Voir le message
    à moins d'avoir une très bonne raison (utilisation de pymedia par exemple), la version 2 tend à disparaître.
    En fait pas mal des modules déployés sur notre système sont des modules "métier", partagés par différentes applications, différents systèmes, entre autres : osx leopard (2.5), snow leopard (2.6), lion (2.7), windows (2.6), maya 2009 (2.5 custom), maya 2010+ (2.6 custom), tractor (2.6 système), nuke 6.0 (2.5 custom), nuke 6.2+ (2.6 custom) etc.

    L'évolution des versions de base sur les os se fait en décalé entre les postes de travail et les serveurs (toujours plus ancien sur les serveurs, surtout osx), et de même pour les applications, sachant qu'on conserve toujours au moins deux ans de "backward compatibility" pour pouvoir ressortir des prods dans leur contexte applicatif/pipe d'origine.

    Comme on est une toute petite équipe, que le noyau python n'est pas le seul sujet d'occupation, et que je me vois mal maintenir des branches du pipeline pour chaque version majeure (sans compter l'enfer pour la gestion des environnements) ... j'essaye de faire en sorte que ça passe partout !

    Python 3 risque de s'imposer d'ici peu niveau os, c'est pourquoi j'essaye de l'anticiper doucement, mais on reste fortement ancré autour du 2.5 / 2.6 et ça ne risque pas de bouger tout de suite ...

    Tonio

  17. #17
    Candidat au Club
    Homme Profil pro
    Directeur technique
    Inscrit en
    Août 2012
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Directeur technique
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Août 2012
    Messages : 6
    Points : 2
    Points
    2
    Par défaut
    Nouvel essai, j'ai simplifié le testcase

    l'idée reste d'être capable de déclencher du code (par défaut une exception) lors de l'accès à certains attributs :

    - pour des objets de haut niveau uniquement (pour le traitement de datas pures j'ai des structures plus adaptées quand même ...)
    - dans le but d'y mettre en place une traçabilité et un système de validation des logiques qui lient ces classes on va dire
    - certainement pas pour bloquer les dev ce qui de toute façon est impossible en python
    - mais en gardant en tête que les niveaux sont très disparates, et que sans parler des conventions qui sont loin d'être connues de tous, plus la console d'erreur est explicite, moins il y a de questions ^^

    Pour la version "décorateur de classes" VV33D, argl, dispo uniquement en python 2.6+ donc j'oublie pour l'instant ... je crois qu'il ne reste plus que l'affectation des properties à l'extérieur de la classe, quitte à faire un
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    class A:
        def foo(self): pass
        ...
    declare_attributes(A, private=("foo", "bar"), protected=("toto", "titi"))
    Pour la curiosité j'essaye de jouer avec les metaclass mais ça n'est pas gagné, soit je comprends mal comment c'est supposé marcher, soit je fais de travers, soit les deux ;p

    voici une nettoyée de toute notion de protection, juste pour le principe de passer par un setter et essayer de récupérer la classe d'origine d'un attribut :

    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
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
     
    def safe_setattr(self, name, value, base_class):
        """setattr accessor, checking for base_class permissions"""
        print "setattr " + name + " base " + str(base_class)
        setattr(self, name, value)
     
    def safe_decorator(func, base_class=None):
        """attributes decorator, force safe_setattr on affect"""
     
        # add "_" to the attribute name to avoid clash with the property                                                                      
        name = "_" + func.__name__
     
        fget = lambda self: getattr(self, name)
        fset = lambda self, value: safe_setattr(self, name, value, base_class)
        fdel = lambda self: delattr(self, name)
     
        # store the base function, to regenerate this decorator later                                                                         
        fget.base_func = func
     
        return property(fget, fset, fdel, "docstring for " + func.__name__)
     
    def replace_props(dct, cls):
        """replace properties : safe_decorator with the source class of each attribute"""
        ret = dict()
        for k, v in dct.iteritems():
            if type(v) == property:
                print "forcing base class " + str(cls) + " for " + k
                ret[k] = safe_decorator(v.fget.base_func, cls)
            else:
                ret[k] = v
        return ret
     
    class meta(type):
        """meta class, should replace properties with the right class object """
     
        def __new__(cls, name, bases, dct):
            print "meta __new__ named '" + name + "' class " + str(cls)
            # broken here, cls is __main__.meta wich is not what we're lookin for                                                             
     
    	dct = replace_props(dct, cls)
     
            return type.__new__(cls, name, bases, dct)
     
        def __init__(cls, name, bases, dct):
            print "meta __init__ named '" + name + "' class " + str(cls.__class__)
            # broken here, cls (self) is __main__.base_class                                                                                 
            # but properties are not replaced in created objects                                                                              
     
            # dct = replace_props(dct, cls)                                                                                                   
     
            super(meta, cls).__init__(name, bases, dct)
     
    class base_class(object):
        __metaclass__ = meta
     
        def __init__(self):
            print "base __init__"
            self._attr = 42
     
        @safe_decorator
        def attr():
            """place holder for an attribute _attr with attr() property"""
            pass
     
    class sub_class(base_class):
        pass
     
    if __name__ == "__main__":
        obj = base_class()
        obj.attr = 42
        obj = sub_class()
        obj.attr = 42
    Merci encore pour toutes vos pistes !

    Tonio

  18. #18
    Expert éminent
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    3 939
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 3 939
    Points : 7 348
    Points
    7 348
    Par défaut
    Plus je lis vos posts, plus j'ai de difficultés à comprendre ce que vous souhaitez réellement faire.

    Vous cherchez à modifier une classe, tout en gardant les attributs privés/protégés de cette classe?

  19. #19
    Candidat au Club
    Homme Profil pro
    Directeur technique
    Inscrit en
    Août 2012
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Directeur technique
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Août 2012
    Messages : 6
    Points : 2
    Points
    2
    Par défaut
    Bonjour Fred, désolé si je suis confus, la clarté n'a jamais été mon fort ...

    Je cherche à implémenter la notion de membre private/protected sur un ensemble de classes existantes, donc le plus légèrement possible, et avec les contraintes "contextuelles" listées dans le premier post. Peu importe pour moi le moyen d'y arriver, mais si c'est élégant et efficace tant mieux

    Pour ce faire, je dois pouvoir déterminer quelle est la classe "d'origine" d'un attribut (et pas la classe d'un objet qui en aurait hérité pour le protected), et celle de l'objet qui a cherché à le modifier, pour déterminer si l'accès est autorisé ou non.

    J'ai essayé d'utiliser les décorateurs dans ce but, ça me semblait approprié et pas trop lourd en inférence, mais ça a bloqué car un décorateur ne sait absolument pas, au moment de sa construction, à quelle classe il est en train de participer ... et il est hors de question que je le détermine à l'exécution !

    La proposition de wiztricks d'utiliser une metaclass pour relayer cette information (si j'ai bien compris l'idée ? :/) semblait prometteuse mais je bloque encore à la mise en oeuvre.

    La solution de VV33D consistant à affecter les décorateurs après construction de la classe me semble finalement la plus jouable, et light en syntaxe et code, je pense me diriger vers ça.

  20. #20
    Expert éminent
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    3 939
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 3 939
    Points : 7 348
    Points
    7 348
    Par défaut
    Je ne suis pas sûr, car j'en ai jamais eu besoin, cela doit s'appeler de l'introspection.

    Le module inspect dans ce cas pourrait vous être utile. Il existe depuis la version 2.2

    Pour annoncer l'origine d'un objet, on peut créer une fonction du style

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    def print_class(obj, attr):
        try:
            getattr(obj, attr)
            print "l'attribut %s fait partie de \
    la classe %s" %(attr, obj.__class__.__name__)
        except AttributeError:
            return None

Discussions similaires

  1. [reflexion] membre private & protected
    Par ZaaN dans le forum C#
    Réponses: 2
    Dernier message: 28/03/2008, 16h21
  2. Réponses: 3
    Dernier message: 12/01/2006, 22h26
  3. [POO] vider un array membre de classe
    Par jlf dans le forum Langage
    Réponses: 2
    Dernier message: 20/10/2005, 11h54
  4. Problèmes de fonctions membres de classe templates, gcc3.3.6
    Par yves.dessertine dans le forum Autres éditeurs
    Réponses: 12
    Dernier message: 17/10/2005, 22h36
  5. Private - Protected
    Par Argonz dans le forum C++
    Réponses: 11
    Dernier message: 06/08/2004, 17h21

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