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

    Même si la syntaxe décorateur n'existe qu'à parti de py26 (il me semblait que c'était plus vieux), rien n’empêche de l'écrire en fonctionnel:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    class A: pass
    A= access(private= 'foo', protected= '__init__' ) (A) # décoration sans @
    Pour ce faire, je dois pouvoir déterminer quelle est la classe "d'origine" d'un attribut
    Ca marchera pour les attributs de classe ou les méthodes, mais pour les attributs de l'instance (le __dict__), la situation sera plus compliquée car il faut déterminer à quelle classe appartient la méthode ayant crée l'attribut !

    Sinon, je serais intéressé par vos réactions à cette idée:
    Cela aurait il du sens de définir les accès relativement à l'instance, et non à la classe ?

  2. #22
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Mars 2007
    Messages
    941
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2007
    Messages : 941
    Points : 1 384
    Points
    1 384
    Par défaut
    Je me suis un peu amusé avec cela. Mon idée est d'utiliser des descripteurs pour contrôler l'accès aux attributs et une méta-classe pour associer ces descripteurs avec la classe dans laquelle ils sont initialement définis.

    J'ai un peu changé la spécification par rapport à ce que TOON3 voulait; mais c'est je pense plus cohérent et plus proche de ce qu'on trouve dans d'autres langages: en l'occurence, MySubClass().assign() ne lève pas d'erreur, car la méthode assign est définie dans MyClass et n'est pas redéfinie dans MySubClass. Par contre, MySubClass().bad_assign() (voir code) lève bien une erreur.

    Voilà où j'en suis:
    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
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    from functools import wraps
    import inspect
    import types
     
    def access_wrap(f):
        "decorator which checks accessibility of a method by calling the access method of the object"
        @wraps(f)
        def wrapper(self, *args):
            if self.access(get_lexical_call_class()):
                return f(self, *args)
            else:
                raise AttributeError("Scope Error")
        return wrapper
     
     
    class safe_descriptor(object):
        "base class for accessibility-constrained descriptors"
        def __init__(self, value=None):
            self._value = {}       # dictionary which maps instances id's to values
            self._default = value
            self._owner = None     # filled-in by the metaclass
     
        @access_wrap
        def __get__(self, instance, owner):
            val = self._value.get(id(instance), self._default)
            if callable(val):
                # if val is a function, bind it to the instance
                return types.MethodType(val, instance)
            else:
                return val
     
        @access_wrap
        def __set__(self, instance, value):
            self._value[id(instance)] = value
     
        @access_wrap
        def __delete__(self, instance):
            if id(instance) in self._value:
                del self._value[id(instance)]
     
    class public(safe_descriptor):
        def access(self, caller):
            return True
     
    class private(safe_descriptor):
        def access(self, caller):
            return caller and caller == self._owner
     
    class protected(safe_descriptor):
        def access(self, caller):
            return caller and issubclass(caller, self._owner)
     
     
    class SafeMeta(type):
        def __new__(cls, name, bases, attrs):
            kls = type.__new__(cls, name, bases, attrs)
            for v in attrs.values():
                if isinstance(v, safe_descriptor):
                    v._owner = kls
            return kls
     
    class safe_object(object):
        __metaclass__ = SafeMeta
     
     
    def get_lexical_call_class(depth=1):
        depth += 1
        stack = inspect.stack()
        if stack and len(stack) > depth and stack[depth]:
            frame,_,_,name,_,_ = stack[depth]
            cls = get_frame_class(frame)
            return cls and get_definition_class(name, cls)
        return None
     
    def get_frame_class(obj):
        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
     
    def get_definition_class(name, cls):
        for c in inspect.getmro(cls):
            if name in c.__dict__:
                return c
        return None
     
     
    # tests
    if __name__ == '__main__':
     
        class MyClass(safe_object):
            pub = public()
            prot = protected()
            priv = private()
     
            def __init__(self):
                self.pub = 42
                self.prot = 42
                self.priv = 42
     
            def assign(self):
                self.pub = 666
                self.prot = 666
                self.priv = 666
     
            @protected
            def prot_method(self):
                return 42
     
            @private
            def priv_method(self):
                return 666
     
            @public
            def pub_method(self, n):
                return n + self.prot_method() + self.priv_method()
     
        class MySubClass(MyClass):
            def bad_assign(self):
                self.pub = 666
                self.prot = 666
                self.priv = 666    # erreur
     
            def pub_method(self):
                return self.prot_method()
     
            def bad_method(self):
                return self.priv_method()  #erreur
    Les descripteurs sont utilisables pour déclarer un attribut ainsi que comme décorateurs pour les méthodes.

    Mais il reste quelques problèmes. Dont au moins un gros: la méthode que j'utilise pour déterminer les accès ne fonctionnent pas si on utilise super dans une sous-classe...
    Par exemple, si on ajoute ceci dans MySubClass:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
            def __init__(self):
                super(MySubClass, self).__init__()
                # MyClass.__init__(self) ne fonctionne pas non plus
    un erreur est levée alors que cela ne devrait pas être le cas.
    Je n'ai pas non plus tenu compte du cas des méthodes de classes ou des méthodes statiques...

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