Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Mais aussi sur les technos front comme React, Angular, Typescript et Javascript en général.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

Protéger un attribut d'une classe, bonne idée ?

+1 vote

Je peaufine un module pour piloter une imprimante thermique.

Ma question est la suivante : est-il raisonnable, utile, d'empêcher l'assignation d'un attribut ? J'en ai plusieurs comme ça, par exemple, il y a des compteurs internes pour garder en mémoire le nombre de lignes imprimées ou encore le nombre maximum de caractères qui peuvent être imprimées sur une seule ligne. Pour le second, c'est assez critique parce que certaines méthodes changent la taille du texte, et donc la variable est mise à jour en fonction. Mais n'importe qui pourrait la modifier.

Un peu de code :

# La classe épurée
class ThermalPrinter(Serial):
    def __init__(self):
        self.max_column = 32

    def size(self, value):
        ''' Changement de la taile du texte. '''
        if size == 'L':
            # Large : double largeur et double hauteur des caractères
            self.max_column = 16  # 32 / 2 :)
        # ...

Et ça s'utilise comme ça :

>>> printer = ThermalPrinter()
>>> printer.max_column
32

>>> printer.size('L')
>>> printer.max_column
16

>>> printer.max_column = 4096
>>> printer.max_column
4096

Pour palier ce problème, je suis passé par des properties :

__max_column = 32

@property
def max_column(self):
    return self.__max_column

@max_column.setter
def max_column(self, *args, **kwargs):
    pass

Je sais bien qu'on peut toujours modifier l'attribut puisque rien n'est réellement privé en Python.

1) Cependant, ça vaut la peine de faire ça ? Sachant que j'ai 5 attributs dans le même cas.
2) Est-ce que je laisse l'attribut accessible et tant pis si le dev fait n'imp' ?
3) Une alternative vous connaissez ?

demandé 27-Sep-2016 par Tiger-222 (498 points)
edité 28-Sep-2016 par Tiger-222

1 Réponse

+2 votes
 
Meilleure réponse

Si il est important pour l'intégrité (i.e. le bon fonctionnement) de l'objet d'éviter une modif de variables internes, oui, ça paraît une bonne idée d'expliciter cette contrainte pour le client par la sémantique du programme.

S'il s'agit d'une limitations du design, renforcer le côté limitation par la sémantique est plutôt une bonne idée (du moins, c'est ce que mon expérience me dit).


L'autre solution, c'est de le dire dans la doc.
Mais personne ne lit vraiment la doc.
Et même si on la lisait, ça serait très irritant de lire des trucs du style si vous appelez cette fonction, alors surtout après ne touchez pas à cet attribut, hein, svp merci.
(D'autant qu'on est en Python, si c'est pour lire des trucs comme ça, on ferait du PHP)

Bien sûr, de base c'est un attribut, le client est pas sensé y toucher.
Mais il y a une différence entre la POO me dit que je viole l'encapsulation et l'auteur du code n'a pas prévu de gérer mes conneries.


Sur le long terme, ça peut payer : l'interface plantera, plutôt que laisser le dev qui débute essayer de comprendre pourquoi ce qu'il fait est nul.
Au besoin, il comprendra bien vite que la doc peut l'aider, et il corrigera son erreur.

Pour le dev expérimenté, c'est pas hyper gênant : il va pouvoir passer au travers. Il sera en hors-piste ; il fait ce qu'il veut, mais la direction décline toute responsabilité en cas d'avalanche.


Enfin, si vraiment t'as pas envie de générer les property à la main, ça reste automatisable.

répondu 27-Sep-2016 par lucas (2,206 points)
sélectionné 28-Sep-2016 par Tiger-222

Merci @lucas, tu me confortes dans mon idée. J'ajouterai une mention dans la doc pour être complet.
Petit détail qui a son importance : penses-tu qu'il est préférable de lever une exception en cas de tentative d'assignation ou je pass en douce ?

By design, une property lèvera une erreur si aucun setter n'existe. Si tu parle de l'attribut privé modifié par un client aimant l'aventure, alors ne l'embête pas avec des protections qui sont sensées viser ceux qui ne connaissent pas l'API.

Donc, il est préférable de ne pas définir @max_column.setter du tout ?

Dans ce cas, pourquoi ne pas obliger l'utilisateur à spécifier le nombre de colonnes dans le constructeur ?

Ou, pourquoi ne pas faire en sorte que le setter de nombre de colonne modifie la taille du texte, et vice-versa ?

Ensuite, si pour une raison ou une autre tu veux empêcher la modification du nombre de colonne,
il faut dans ce cas lever manuellement une erreur quand la méthode n'est pas appelée au bon moment. Le setter est justement fait pour avoir un comportement spécifique lors de l'assignation de l'objet, et lever une exception est possible avec raise.

Enfin, je dirais qu'en Génie Logiciel, on aime pas trop les interfaces de ce type, car elles changent en fonction du contexte.
On préférerais certainement définir deux interfaces différentes et travailler avec l'une ou l'autre, selon les cas, ou alors la technique des deux setters couplés à une bonne doc et du logging.

...