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 :

slots, héritage, références


Sujet :

Python

  1. #1
    Membre confirmé

    Homme Profil pro
    Bidouilleur
    Inscrit en
    Avril 2016
    Messages
    721
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Bidouilleur

    Informations forums :
    Inscription : Avril 2016
    Messages : 721
    Points : 503
    Points
    503
    Billets dans le blog
    1
    Par défaut slots, héritage, références
    Salut,

    J'ai fait quelques tests avec les slots, même si pour le moment j'en ai pas trop besoin, peut-être ultérieurement.
    Bon pour être franc le modèle objet python, j'aime bien certains aspects, mais d'autres je déteste.

    Le fonctionnement lors de l'héritage des slots me laisse vraiment circonspect à tel point que je me demande s'il y a vraiment des programmeurs qui les utilisent vu les contraintes que cela engendre.

    Si on fait un truc basique, ok, c'est cool, ça fonctionne.

    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
    class A :
        __slots__ = ['_var']
        def __init__(self, valeur) : 
            self._var = valeur
     
        @property
        def var(self) :
            return self._var
     
    class B(A) :
        def __init__(self, valeur) :
            A.__init__(self, valeur)
     
    a = A('A')
    b = B('B')
    print(a.var, b.var) # A, B
    Mais dès lors, que l'on veut un peu complexifier.

    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
    class A :
        __slots__ = {'_var':None}
        def __init__(self, valeur) :
            self.__slots__['_var'] = valeur
     
        def __setattr__(self, cle, valeur) :
            raise NotImplementedError
     
        @property
        def var(self) :
            return self.__class__.__slots__['_var']
     
    class B(A) :
        def __init__(self, valeur) :
            A.__init__(self, valeur)
     
    a = A('A')
    b = B('B')
    print(a.var, b.var) # B, B ...
    Ok, la doc est claire, faut ajouter __weakreference__ aux classes filles pour avoir un fonctionnement correct.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    __slots__ = {'__weakreference__':None}
    Mais c'est moisi non ce truc ?
    Y'a pas un subterfuge pour induire ce comportement depuis la classe mère ?

    Et d'ailleurs, je me demande si on ne pourrait pas se servir de ce comportement de références implicite comme singleton.
    Le temps ronge l'amour comme l'acide.

  2. #2
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 283
    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 283
    Points : 36 770
    Points
    36 770
    Par défaut
    Salut,

    Une variable de classe mutable reste une variable de classe mutable (quelque soit la façon tordue que vous utiliser pour y accéder):
    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
     
    class A :
        dd = {'_var':None}
        def __init__(self, valeur) :
            self.dd['_var'] = valeur
     
        def __setattr__(self, cle, valeur) :
            raise NotImplementedError
     
        @property
        def var(self) :
            return self.__class__.dd['_var']
     
    class B(A) :
     
        def __init__(self, valeur) :
            A.__init__(self, valeur)
     
    a = A('A')
    b = B('B')
    print(a.var, b.var) # B, B ...

    Citation Envoyé par bistouille
    Et d'ailleurs, je me demande si on ne pourrait pas se servir de ce comportement de références implicite comme singleton.
    Et un singleton est toujours réalisé avec une(des) variable(s) de classe(s).

    Citation Envoyé par bistouille
    J'ai fait quelques tests avec les slots, même si pour le moment j'en ai pas trop besoin, peut-être ultérieurement.
    Bon pour être franc le modèle objet python, j'aime bien certains aspects, mais d'autres je déteste.
    Je dirais qu'il faut utiliser les slots seulement quand on en a besoin:
    By default, instances of both old and new-style classes have a dictionary for attribute storage. This wastes space for objects having very few instance variables. The space consumption can become acute when creating large numbers of instances.
    Economiser l'espace mémoire lorsqu'on doit créer beaucoup d'instances d'une classe ayant peu d'attributs (exemple: une classe Point ou Vecteur dans un plan pour une bibliothèque 2D/3D).

    Les restrictions dans l'utilisation des slots ne sont pas faciles à prendre en compte dans une étape d'optimisation: il risque d'y avoir pas mal de chose à refaire. Et c'est vrai que le programmeur Python code plutôt avant d'avoir finaliser le design, ce qui lui permet difficilement de profiter de ce genre de fonctionnalités.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  3. #3
    Membre confirmé

    Homme Profil pro
    Bidouilleur
    Inscrit en
    Avril 2016
    Messages
    721
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Bidouilleur

    Informations forums :
    Inscription : Avril 2016
    Messages : 721
    Points : 503
    Points
    503
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par wiztricks Voir le message
    Une variable de classe mutable reste une variable de classe mutable (quelque soit la façon tordue que vous utiliser pour y accéder)
    Mais

    Alors là, j'avais pas bien compris, je ne voyais pas __slots__ comme une variable de classe du tout, je pensais que c'était pour indiquer à python que l'on souhaitait se servir de __slots__ à la place de __dict__, et donc qu'on substituait __slots__ à __dict__ en ayant le même comportement interne dans l'objet sans le côté dynamique de __dict__

    Citation Envoyé par wiztricks Voir le message
    Et un singleton est toujours réalisé avec une(des) variable(s) de classe(s).
    Ça je le savais déjà, mais merci tout de même

    Citation Envoyé par wiztricks Voir le message
    Economiser l'espace mémoire lorsqu'on doit créer beaucoup d'instances d'une classe ayant peu d'attributs (exemple: une classe Point ou Vecteur dans un plan pour une bibliothèque 2D/3D).
    Intéressant, du coup je crois que je vais voir pour l’utiliser dans certains de mes scripts comportant ce cas de figure.

    Merci
    Le temps ronge l'amour comme l'acide.

  4. #4
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 283
    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 283
    Points : 36 770
    Points
    36 770
    Par défaut
    Citation Envoyé par bistouille Voir le message
    Alors là, j'avais pas bien compris, je ne voyais pas __slots__ comme une variable de classe du tout, je pensais que c'était pour indiquer à python que l'on souhaitait se servir de __slots__ à la place de __dict__, et donc qu'on substituait __slots__ à __dict__ en ayant le même comportement interne dans l'objet sans le côté dynamique de __dict__
    En écrivant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    >>> class A:
    ...    __slots__ = ('var',)
    ...
    on crée une variable de classe (__slots__) à laquelle on assigne la liste des noms d'attributs d'instance "autorisés".
    On supprime le __dict__ assigné lors de la création de l'instance et la mécanique se cache dans des "properties":
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    >>> A.var.__set__(a, 1)  ## <=> a.var = 1
    >>> A.var.__get__(a, A) 
    1
    >>>
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  5. #5
    Membre confirmé

    Homme Profil pro
    Bidouilleur
    Inscrit en
    Avril 2016
    Messages
    721
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Bidouilleur

    Informations forums :
    Inscription : Avril 2016
    Messages : 721
    Points : 503
    Points
    503
    Billets dans le blog
    1
    Par défaut
    Je comprends un peu mieux désormais, mais c'est assez déroutant.

    Si on utilise un slot, la variable d'instance garde toujours la référence à la variable de classe, donc si on modifie la variable de classe, la variable d'instance est aussi modifiée, je trouve cela assez dangereux comme comportement, avis qui n'engage que moi.

    Ce qui est marrant, c'est que j'ai vu qu'on pouvait fourrer le __dict__ dans __slots__, après je ne sais pas si __slots__ garde toujours son intérêt en faisant ceci, si c'est le cas, ça peut être intéressant d'utiliser ce contournement aux restrictions des slots.

    Bien merci wiztricks pour ces précisions.
    Le temps ronge l'amour comme l'acide.

  6. #6
    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 bistouille Voir le message
    Si on utilise un slot, la variable d'instance garde toujours la référence à la variable de classe, donc si on modifie la variable de classe, la variable d'instance est aussi modifiée, je trouve cela assez dangereux comme comportement, avis qui n'engage que moi.
    Non ce n'est pas ce qui se passe. Quand Python ne trouve pas une variable d'instance, il cherche une variable de classe du même nom.

    Donc quand tu écris self.__slots__['_var'] = valeur, tu modifies la variable de classe __slots__, car il n'y a pas de variable d'instance de ce nom-là. Le résultat est le même que si tu écris self.__class__.__slots__['_var'] = valeur.

    J'ai l'impression que tu confonds la variable de classe __slots__ avec les attributs d'instances qu'elle définit.

    Ca n'a pas de sens d'assigner un dictionnaire à __slots__. Cela passe car il attend un itérable, et un dictionnaire est un itérable, mais ça a le même effect qu'utiliser une liste contenant les clés du dictionnaire. Les valeurs du dictionnaires sont ignorées. Tu retrouves certes ton dictionnaire dans la variable de classe __slots__, mais, une fois que la classe est créée, c'est une variable de classe ordinaire qui n'a plus de mécanisme particulier associé. Du coup ça n'a pas de sens non plus de modifier dynamiquement les __slots__, ils ne sont pas faits pour cela. Cela n'a pas de répercussion sur les variables d'instances.

  7. #7
    Membre confirmé

    Homme Profil pro
    Bidouilleur
    Inscrit en
    Avril 2016
    Messages
    721
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Bidouilleur

    Informations forums :
    Inscription : Avril 2016
    Messages : 721
    Points : 503
    Points
    503
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par dividee Voir le message
    Non ce n'est pas ce qui se passe. Quand Python ne trouve pas une variable d'instance, il cherche une variable de classe du même nom.

    Donc quand tu écris self.__slots__['_var'] = valeur, tu modifies la variable de classe __slots__, car il n'y a pas de variable d'instance de ce nom-là. Le résultat est le même que si tu écris self.__class__.__slots__['_var'] = valeur.

    J'ai l'impression que tu confonds la variable de classe __slots__ avec les attributs d'instances qu'elle définit.
    Moi je veux bien, mais quand je faisais allusion à ça, c'était :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    >>> class Truc :
    ...     __slots__ = ['bidule']
    ...     def __init__(self, valeur) :
    ...         self.bidule = valeur
    ... 
    >>> truc = Truc(5)
    >>> truc.bidule
    5
    >>> Truc.bidule = 10
    >>> truc.bidule
    10
    Alors soit y'a encore un truc que j'ai pas pigé, soit le comportement est vraiment comme ça.

    Citation Envoyé par dividee Voir le message
    Ca n'a pas de sens d'assigner un dictionnaire à __slots__. Cela passe car il attend un itérable, et un dictionnaire est un itérable, mais ça a le même effect qu'utiliser une liste contenant les clés du dictionnaire. Les valeurs du dictionnaires sont ignorées. Tu retrouves certes ton dictionnaire dans la variable de classe __slots__, mais, une fois que la classe est créée, c'est une variable de classe ordinaire qui n'a plus de mécanisme particulier associé. Du coup ça n'a pas de sens non plus de modifier dynamiquement les __slots__, ils ne sont pas faits pour cela. Cela n'a pas de répercussion sur les variables d'instances.
    Bah, c'est ce que je voulais savoir, d'où ma question, mais je comprend rien à ce que tu as tenté de m'expliquer

    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
    >>> class Truc :
    ...     __slots__ = ['bidule']
    ...     def __init__(self) :
    ...         self.bidule = 5
    ...         self.machin = 10
    ... 
    >>> truc = Truc()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 5, in __init__
    AttributeError: 'Truc' object has no attribute 'machin'
    >>> 
    >>> 
    >>> class Truc :
    ...     __slots__ = ['bidule', '__dict__']
    ...     def __init__(self) :
    ...         self.bidule = 5
    ...         self.machin = 10
    ... 
    >>> truc = Truc()    
    >>> truc.machin
    10
    Ma question était surtout sur l"incidence que cela provoque sur l'utilisation de slots, si cela annihile ses avantages.
    Le temps ronge l'amour comme l'acide.

  8. #8
    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 bistouille Voir le message
    Moi je veux bien, mais quand je faisais allusion à ça, c'était :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    >>> class Truc :
    ...     __slots__ = ['bidule']
    ...     def __init__(self, valeur) :
    ...         self.bidule = valeur
    ... 
    >>> truc = Truc(5)
    >>> truc.bidule
    5
    >>> Truc.bidule = 10
    >>> truc.bidule
    10
    Alors soit y'a encore un truc que j'ai pas pigé, soit le comportement est vraiment comme ça.
    OK j'avais pas bien compris. Ce comportement est dû à la façon dont les slots sont implémentés au moyen de descripteurs. C'est ce dont parlait wiztricks ci-dessus: quand tu écris self.bidule = valeur, c'est traduit en Truc.bidule.__set__(self, valeur). "bidule" est une variable de classe qui contient un descripteur (un objet avec des méthodes __get__, __set__ et __delete__). Si tu écrases ce descripteur par autre chose, en écrivant Truc.bidule = 10, cela ne fonctionne plus; le "slot" a disparu et il ne reste qu'une variable de classe avec la valeur 10.


    Citation Envoyé par bistouille Voir le message
    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
    >>> class Truc :
    ...     __slots__ = ['bidule']
    ...     def __init__(self) :
    ...         self.bidule = 5
    ...         self.machin = 10
    ... 
    >>> truc = Truc()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 5, in __init__
    AttributeError: 'Truc' object has no attribute 'machin'
    >>> 
    >>> 
    >>> class Truc :
    ...     __slots__ = ['bidule', '__dict__']
    ...     def __init__(self) :
    ...         self.bidule = 5
    ...         self.machin = 10
    ... 
    >>> truc = Truc()    
    >>> truc.machin
    10
    Ma question était surtout sur l"incidence que cela provoque sur l'utilisation de slots, si cela annihile ses avantages.
    Je peux me tromper, mais il me semble effectivement que ça n'a pas grand intérêt...

  9. #9
    Membre confirmé

    Homme Profil pro
    Bidouilleur
    Inscrit en
    Avril 2016
    Messages
    721
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Bidouilleur

    Informations forums :
    Inscription : Avril 2016
    Messages : 721
    Points : 503
    Points
    503
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par dividee Voir le message
    OK j'avais pas bien compris. Ce comportement est dû à la façon dont les slots sont implémentés au moyen de descripteurs. C'est ce dont parlait wiztricks ci-dessus: quand tu écris self.bidule = valeur, c'est traduit en Truc.bidule.__set__(self, valeur). "bidule" est une variable de classe qui contient un descripteur (un objet avec des méthodes __get__, __set__ et __delete__). Si tu écrases ce descripteur par autre chose, en écrivant Truc.bidule = 10, cela ne fonctionne plus; le "slot" a disparu et il ne reste qu'une variable de classe avec la valeur 10.
    C'est donc bien ce que j'avais compris même si ma formulation n'était pas vraiment exacte, t'façon, si j'avais dit une grosse connerie, je pense que wiztricks m'aurait repris

    Citation Envoyé par dividee Voir le message
    Je peux me tromper, mais il me semble effectivement que ça n'a pas grand intérêt...
    L'avantage, serait quand même d'éviter à déclarer dans le slot une variable chaque fois qu'on a besoin d'une dans la classe, en gros garder la déclaration dynamique d'attributs.

    Mais à mon avis, cela a une incidence, car le comportement diffère, et on retrouve le comportement standard, ce qui est logique.
    J'avais pas pensé à tester.

    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
    >>> class Truc :
    ...     __slots__ = ['__dict__']
    ...     def __init__(self, valeur) :
    ...         self.bidule = valeur
    ... 
    >>> t = Truc(10)
    >>> t.bidule
    10
    >>> Truc.bidule
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: type object 'Truc' has no attribute 'bidule'
    >>> Truc.bidule = 20
    >>> Truc.bidule
    20
    >>> t.bidule
    10
    J'essaierais de faire des tests sur l'occupation mémoire, enfin si ça a un intérêt quelconque d'effectuer une comparaison.
    Le temps ronge l'amour comme l'acide.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 21
    Dernier message: 26/07/2011, 12h08
  2. Signals/Slots et héritage
    Par agh dans le forum Débuter
    Réponses: 1
    Dernier message: 18/04/2010, 16h36
  3. Réponses: 2
    Dernier message: 16/06/2009, 16h41
  4. Signals, slots et héritage
    Par Niak74 dans le forum Qt
    Réponses: 14
    Dernier message: 26/02/2009, 15h33
  5. Passge par référence & héritage
    Par jsatla dans le forum C++
    Réponses: 11
    Dernier message: 04/01/2006, 17h44

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