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 :

utilité des classes, différence entre classe et fonction


Sujet :

Python

  1. #41
    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
    Le terme "descripteur" a un sens particulier en Python, c'est le mécanisme utilisé pour implémenter plein d'autres fonctionnalités, notamment les properties mais pas seulement. Voir par exemple: How-To Guide for Descriptors

    Cela dit, je n'ai jamais trouvé l'occasion de les utiliser directement. Il semble toujours plus simple de passer par un mécanisme de plus haut niveau (properties, decorateurs, ...). Je serais curieux de voir un exemple qui les exploitent directement (mais que ce soit justifié).

  2. #42
    Membre éclairé
    Homme Profil pro
    heu...
    Inscrit en
    Octobre 2007
    Messages
    648
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : heu...

    Informations forums :
    Inscription : Octobre 2007
    Messages : 648
    Points : 773
    Points
    773
    Par défaut
    Comme (souvent) en python il existe plusieurs manière d'arriver à un résultat, je ne sais pas trop si dans l'exemple suivant, l'emploi de Descriptor est pleinement justifié, néanmoins, c'est jusqu'à ce jour, l'utilisation la plus "justifiée" que j'ai pu en faire :
    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
    class MetaTyped(type):
        '''Metaclass of TypedClass based class'''
        def __new__(cls,nm,bs,at):
            for key in at:
                if any([x!='__' for x in (key[:2], key[-2:])]) and 'function' not in `type(at[key])`:
                    at[key]=Des(at[key])
            return type.__new__(cls,nm,bs,at)
     
    class Des(object):
        '''Descriptor that allow to set a value on itself (and so "act" as a class attribute)
        or on an instance of another class. The allowed type of value is the type of the value
        used at the __init__'''
        def __init__(s,v,name=None):
            s.val   = v
            s.type  = type(v)
            s.name  = name
        def __get__(s, obj, objtype=None):
            name = s.name
            if name is None or name not in obj.__dict__:
                return s.val
            return obj.__dict__[name]
        def __set__(s,obj,val):
            if type(val)!=s.type    : raise TypeError('expected %s, have %s'%(s.type,type(val)))
            elif s.name is None     : s.val=val
            else                    : setattr(obj,s.name,val)
     
    class TypedClass(object):
        '''class designed to be subclassed. All class attributes and instance attributes are
        "typed restricted"
        NOTE:
            _ if __setattr__ is overloaded, do it carefully, or the instances won\'t be "typed
              restricted" anymore...  -_-
            _ creating an instance attribute, will not only define its type, but also
              its default value - as other instances (past or future) will be able to
              access to an attribute with the same name
            _ it is not recommended to set class attributes outside the class
              definition, as in order for it to be typed restricted, the value
              should be a Des instance'''
        __metaclass__=MetaTyped
        def __setattr__(s,nm,v):
            if nm[:6]=='_desc_' or nm in s.__class__.__dict__:
                object.__setattr__(s,nm,v)
            elif nm not in s.__class__.__dict__:
                setattr(s.__class__,nm,Des(v,'_desc_'+nm))
                object.__setattr__(s,'_desc_'+nm,v)
    L'utilité est de faire des sous-classes de TypedClass, dont chaque attributs sera limité à un type bien précis. (soulignons au passage la faible utilité, ainsi que les performances probablement déplorables en cas d'usage intensif)
    ex :
    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
    >>> from p2x.Typed import TypedClass as TC
    >>> class Foo(TC):
    	x=52.5
    	def __init__(s,v):
    		s.y=v
     
     
    >>> a=Foo('a')
    >>> b=Foo('b')
    >>> c=Foo(2)
     
    Traceback (most recent call last):
      File "<pyshell#21>", line 1, in <module>
        c=Foo(2)
      File "<pyshell#18>", line 4, in __init__
        s.y=v
      File "D:\raw\cmn\prog\python_lib\p2x\Typed.py", line 39, in __setattr__
        object.__setattr__(s,nm,v)
      File "D:\raw\cmn\prog\python_lib\p2x\Typed.py", line 23, in __set__
        if type(val)!=s.type    : raise TypeError('expected %s, have %s'%(s.type,type(val)))
    TypeError: expected <type 'str'>, have <type 'int'>
    >>> a.y
    'a'
    >>> b.y
    'b'
    >>> a.z=56
    >>> a.z=130
    >>> b.z
    56
    >>> a.z
    130
    >>> a.x
    52.5
    >>> b.x=46.
    >>> a.x
    46.0

  3. #43
    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
    Merci N.tox, c'est un exemple intéressant, même si je trouve la sémantique des "classes typées" quelque peu surprenante. Je verrais plus un mécanisme basé sur un déclaration au niveau de la classe de tous les attributs typés, un peu comme le _fields_ de ctypes.

  4. #44
    Membre éclairé
    Homme Profil pro
    heu...
    Inscrit en
    Octobre 2007
    Messages
    648
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : heu...

    Informations forums :
    Inscription : Octobre 2007
    Messages : 648
    Points : 773
    Points
    773
    Par défaut
    Je ne suis pas sûr de bien comprendre... Pourrais-tu illustrer ta pensée d'un exemple ? Je suis positivement curieux.

  5. #45
    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
    Citation Envoyé par N.tox Voir le message
    Je ne suis pas sûr de bien comprendre... Pourrais-tu illustrer ta pensée d'un exemple ? Je suis positivement curieux.
    Le principe est que le type est déclaré explicitement à la création de la classe, au lieu d'être fixé à la première assignation. C'est en fait plus proche de ce qu'on a dans un langage statiquement typé.

    Quelque chose comme ceci (ce n'est qu'une ébauche):
    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
    # module typed
    __all__ = ['TypedClass']
     
    class MetaTyped(type):
        def __new__(cls, name, bases, attrs):
            if '_fields_' in attrs:
                for a in attrs['_fields_']:
                    if len(a) == 2:
                        nm, typ = a
                        attrs[nm] = TypedAttribute(name, nm, typ)
                    elif len(a) == 3:
                        nm, typ, default = a
                        attrs[nm] = TypedAttribute(name, nm, typ, default=default)
                    else:
                        raise TypeError('Bad entry in _fields_')
            return type.__new__(cls, name, bases, attrs)
     
    class TypedAttribute(object):
        def __init__(self, owner_name, attr_name, typ, **kwargs):
            self.owner_name = owner_name
            self.attr_name = attr_name
            self.typ = typ
            if 'default' in kwargs:
                self.default = kwargs['default']
     
        def __repr__(self):
            if hasattr(self, 'default'):
                return "<TypedAttribute '%s' of class '%s', type %s, default %s>" % \
                       (self.attr_name, self.owner_name, self.typ, self.default)
            else:
                return "<TypedAttribute '%s' of class '%s', type %s>" % \
                       (self.attr_name, self.owner_name, self.typ)
     
        def __get__(self, inst, owner):
            if inst is None: # class attribute access
                return self
            else: # instance attribute access
                attr = self.mangled_name()
                if hasattr(inst, attr):
                    return getattr(inst, attr)
                elif hasattr(self, 'default'):
                    return self.default
                else:
                    raise NameError("attribute '%s' referenced before assignment" %
                                    self.attr_name)
     
        def __set__(self, inst, value):
            if not isinstance(value, self.typ):
                raise TypeError('expected value of type %s, got %s instead' %
                                (self.typ, repr(value)))
            attr = self.mangled_name()
            setattr(inst, attr, value)
     
        def mangled_name(self):
            return '_' + self.attr_name
     
    class TypedClass(object):
        __metaclass__ = MetaTyped
    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
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    >>> from typed import TypedClass
    >>> class Foo(TypedClass):
    ... 	_fields_ = [('x', float, 52.5),
    ... 		    ('y', str)]
    ... 	def __init__(self, v=None):
    ... 		if v: self.y = v
    ...
    >>> Foo.x
    <TypedAttribute 'x' of class 'Foo', type <type 'float'>, default 52.5>
    >>> Foo.y
    <TypedAttribute 'y' of class 'Foo', type <type 'str'>>
    >>> a = Foo('a')
    >>> b = Foo('b')
    >>> c = Foo(2)
    Traceback (most recent call last):
      File "<interactive input>", line 1, in <module>
      File "<interactive input>", line 5, in __init__
      File "c:\prog\python\typed.py", line 49, in __set__
        (self.typ, repr(value)))
    TypeError: expected value of type <type 'str'>, got 2 instead
    >>> a.y
    'a'
    >>> b.y
    'b'
    >>> c = Foo()
    >>> c.y
    Traceback (most recent call last):
      File "<interactive input>", line 1, in <module>
      File "c:\prog\python\typed.py", line 44, in __get__
        self.attr_name)
    NameError: attribute 'y' referenced before assignment
    >>> c.x = 12
    Traceback (most recent call last):
      File "<interactive input>", line 1, in <module>
      File "c:\prog\python\typed.py", line 49, in __set__
        (self.typ, repr(value)))
    TypeError: expected value of type <type 'float'>, got 12 instead
    Maintenant, on peut imaginer d'autres façon de déclarer la classe qu'avec un attribut _fields_.
    Par exemple (il faudrait modifier la définition de TypedAttribute):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    class Foo(object):
        x = TypedAttribute(float, default=52.5)
        y = TypedAttribute(str)
    C'est peut-être plus simple, et ne nécessite pas de metaclass.

  6. #46
    Membre éclairé
    Homme Profil pro
    heu...
    Inscrit en
    Octobre 2007
    Messages
    648
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : heu...

    Informations forums :
    Inscription : Octobre 2007
    Messages : 648
    Points : 773
    Points
    773
    Par défaut
    C'est bien ce que je pensais alors... J'y avais pensé mais j'ai préféré quelque chose de plus invisible pour le programmeur utilisateur (en fait, uniquement moi -_-), mais surtout le moins fatiguant pour les doigts .

    Et puis là je viens de tester une théorie sur les métaclasses, qui est tout à fait logique, mais que je n'avais jamais pu remarquer sur les différents codes que j'ai pu voir en relation avec ces dernières. Et donc, j'ai amélioré mon code :
    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
     
    def _debug(txt='',*args):
        if _debug._mode : print txt%args
    def mode(obj):
        _debug._mode=bool(obj)
    _debug.mode=mode ; del mode ; _debug.mode(0)
     
     
    class MetaTypedDyn(type):
        '''Metaclass of TypedClass based class'''
        def __new__(cls,nm,bs,at):
            _debug('MetaTyped.__new__%s',(cls,nm,bs,at))
            for key in at:
                if any([x!='__' for x in (key[:2], key[-2:])]) and 'function' not in `type(at[key])`:
                    at[key]=TypedAttr(at[key])
            return type.__new__(cls,nm,bs,at)
        def __getattribute__(cls, key):
            _debug('MetaTyped.__getattribute__%s',(cls,key))
            cls_attr=type.__getattribute__(cls,key)
            if type(cls_attr)==TypedAttr:
                return cls_attr.val
            return cls_attr
        def __setattr__(cls, key, val):
            _debug('MetaTyped.__setattr__%s',(cls,key,val))
            if key in cls.__dict__:
                cls_attr=type.__getattribute__(cls,key)
                if type(cls_attr)==TypedAttr:
                    cls_attr.__set__(None,val)
                    return None
                raise AttributeError('Attribute %s.%s is type %s, it cannot be changed en route'%(cls,key, type(cls_attr)))
            type.__setattr__(cls,key,TypedAttr(val))
     
     
     
    class TypedAttr(object):
        '''TypedAttr is a Descriptor. The allowed type of value is the type of the value
        used at the __init__, meaning that it is automaticaly defined. Passing a name arg
        at __init__ will do the TypedAttr instance act as an instance attribute (with a 
        default value).'''
        def __init__(s,v,name=None):
            _debug('TypedAttr.__init__%s',(s,v,name))
            s.val   = v
            s.type  = type(v)
            s.name  = name
        def __get__(s, obj, objtype=None):
            _debug('TypedAttr.__get__%s',(s,obj,objtype))
            name = s.name
            if name is None                 : return s      #class attribute access
            elif name not in obj.__dict__   : return s.val  #instance attribute default acces
            return obj.__dict__[name]                       #instance attribute acces
        def __set__(s,obj,val):
            _debug('TypedAttr.__get__%s',(s,obj,val))
            if type(val)!=s.type    : raise TypeError('expected %s, have %s'%(s.type,type(val)))
            elif s.name is None     : s.val=val                 #class
            else                    : setattr(obj,s.name,val)   #instance
     
    class TypedClass(object):
        '''class designed to be subclassed. All class attributes and instance attributes are
        "typed restricted"
        NOTE: if __setattr__ is overloaded, TypedClass.__setattr__ must be called !'''
        __metaclass__=MetaTypedDyn
        def __setattr__(s,nm,v):
            if nm[:6]=='_desc_' or nm in s.__class__.__dict__:
                object.__setattr__(s,nm,v)
            elif nm not in s.__class__.__dict__:
                setattr(s.__class__,nm,TypedAttr(v,'_desc_'+nm))
                object.__setattr__(s,'_desc_'+nm,v)
    La différence, c'est que maintenant pour une classe Foo héritant de TypedClass, "Foo.b=56" créera automatiquement un TypedAttr -d'ailleurs merci pour le coup du "return self" dans TypedAttr.__get__ -. Du coup, je peut maintenant me servir le temps du developpement et des test de TypedClass come base pour mes classes, puis une fois tout fini et testé, remplacer TypedClass par object.

Discussions similaires

  1. Réponses: 12
    Dernier message: 20/05/2009, 15h32
  2. Réponses: 1
    Dernier message: 11/05/2009, 17h39
  3. Conventions en matière de contrôle des paramètres en entrée d'une fonction
    Par Jimalexp dans le forum Algorithmes et structures de données
    Réponses: 0
    Dernier message: 16/01/2009, 20h21
  4. Réponses: 5
    Dernier message: 30/09/2008, 13h36
  5. Mysql5: différences entre procédures et fonctions
    Par El Riiico dans le forum SQL Procédural
    Réponses: 1
    Dernier message: 25/11/2005, 05h43

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