web-dev-qa-db-fra.com

attribut "public" ou "privé" dans Python? Quelle est la meilleure façon?

En Python, j'ai l'exemple de classe suivant:

class Foo:
    self._attr = 0

    @property
    def attr(self):
        return self._attr

    @attr.setter
    def attr(self, value):
        self._attr = value

    @attr.deleter
    def attr(self):
        del self._attr

Comme vous pouvez le voir, j'ai un simple attribut "privé" "_attr" et une propriété pour y accéder. Il y a beaucoup de codes pour déclarer un simple attribut privé et je pense que ce n'est pas respecter la philosophie "KISS" de déclarer tous les attributs comme ça.

Alors, pourquoi ne pas déclarer tous mes attributs comme des attributs publics si je n'ai pas besoin d'un getter/setter/deleter particulier?

Ma réponse sera: Parce que le principe d'encapsulation (OOP) dit le contraire!

Quel est le meilleur moyen ?

47
Sandro Munda

Typiquement, Python s'efforce d'adhérer au niform Access Principle . Spécifiquement, l'approche acceptée est:

  • Exposez directement vos variables d'instance, en autorisant, par exemple, foo.x = 0, Pas foo.set_x(0)
  • Si vous devez encapsuler les accès à l'intérieur des méthodes, pour une raison quelconque, utilisez @property, Qui préserve la sémantique d'accès. Autrement dit, foo.x = 0 Appelle maintenant foo.set_x(0).

Le principal avantage de cette approche est que l'appelant peut le faire:

foo.x += 1

même si le code peut vraiment faire:

foo.set_x(foo.get_x() + 1)

La première déclaration est infiniment plus lisible. Pourtant, avec les propriétés, vous pouvez ajouter (au début ou plus tard) le contrôle d'accès que vous obtenez avec la deuxième approche.

Notez également que les variables d'instance commençant par un seul trait de soulignement sont classiquement privées. C'est-à-dire que le soulignement signale aux autres développeurs que vous considérez la valeur comme privée, et qu'ils ne devraient pas jouer avec elle directement; cependant, rien dans la langue ne les empêche de les déranger directement.

Si vous utilisez un trait de soulignement double (par exemple, __x), Python fait un peu d'obscurcissement du nom. La variable est toujours accessible de l'extérieur de la classe, via son nom obscurci) Cependant, ce n'est pas vraiment privé. C'est juste un peu ... plus opaque. Et il y a des arguments valables contre l'utilisation du double soulignement; d'une part, cela peut rendre le débogage plus difficile.

81
Brian Clapper

Le "dunder" (double soulignement, __) le préfixe empêche l'accès à l'attribut, sauf par le biais d'accesseurs.

class Foo():
    def __init__(self):
        self.__attr = 0

    @property
    def attr(self):  
        return self.__attr

    @attr.setter
    def attr(self, value):
        self.__attr = value

    @attr.deleter
    def attr(self):
        del self.__attr

Quelques exemples:

>>> f = Foo()
>>> f.__attr                          # Not directly accessible.
Traceback (most recent call last):
  File "<input>", line 1, in <module>
AttributeError: 'Foo' object has no attribute '__attr'
>>> '__attr' in f.__dir__()           # Not listed by __dir__()
False
>>> f.__getattribute__('__attr')      # Not listed by __getattribute__()
Traceback (most recent call last):
  File "<input>", line 1, in <module>
AttributeError: 'Foo' object has no attribute '__attr'
>>> f.attr                            # Accessible by implemented getter.
0
>>> f.attr = 'Presto'                 # Can be set by implemented setter.
>>> f.attr
'Presto'
>>> f.__attr = 'Tricky?'              # Can we set it explicitly?
>>> f.attr                            # No. By doing that we have created a 
'Presto'                              # new but unrelated attribute, same name.
14
brannerchinese

Tout simplement, les principes OOP sont faux. Pourquoi c'est une longue discussion qui mène à des guerres enflammées et est probablement hors sujet pour ce site. :-)

Dans Python il n'y a pas d'attributs privés, vous ne pouvez pas les protéger, et ce n'est jamais un vrai problème. Alors ne le faites pas. Facile! :)

Vient ensuite la question: si vous avez un trait de soulignement ou non. Et dans l'exemple que vous avez ici, vous ne devriez certainement pas. Un trait de soulignement principal dans Python est une convention pour montrer que quelque chose est interne, et ne fait pas partie de l'API, et que vous devez l'utiliser à vos propres risques. Ce n'est évidemment pas le cas ici , mais c'est une convention commune et utile.

10
Lennart Regebro

Python n'a pas d'attributs publics OR privés. Tous les attributs sont accessibles à tout le code.

self.attr = 0 #Done

Votre méthode ne rend en rien _attr privé, c'est juste un peu d'obscurcissement.

6
Tyler Eaves

Voir ce lien: https://docs.python.org/2/tutorial/classes.html

9.6. Variables privées et références de classe locale

Les variables d'instance "privées" auxquelles on ne peut accéder que depuis l'intérieur d'un objet n'existent pas en Python. Cependant, il existe une convention qui est suivie par la plupart des Python: un nom préfixé par un trait de soulignement (par exemple _spam) doit être traité comme une partie non publique de l'API (que ce soit un fonction, une méthode ou un membre de données. Il doit être considéré comme un détail d'implémentation et sujet à changement sans préavis.

Puisqu'il existe un cas d'utilisation valide pour les membres privés de classe (à savoir pour éviter les conflits de noms avec des noms définis par des sous-classes), il existe un support limité pour un tel mécanisme, appelé gestion de noms. Tout identifiant de la forme __spam (au moins deux traits de soulignement de tête, au plus un trait de soulignement de fin) est textuellement remplacé par _classname__spam, où classname est le nom de classe actuel avec le ou les traits de soulignement de tête supprimés. Cette manipulation est effectuée sans tenir compte de la position syntaxique de l'identifiant, tant qu'elle se produit dans la définition d'une classe.

4
W.Perrin

Comme d'autres l'ont dit, les attributs privés dans Python ne sont qu'une convention. L'utilisation de la syntaxe property doit être utilisée pour un traitement spécial lorsque les attributs sont liés, modifiés ou supprimés. La beauté of Python est que vous pouvez commencer en utilisant simplement la liaison d'attribut normale, par exemple, self.attr = 0 et si, à une date ultérieure, vous décidez de restreindre la valeur de attr à 0 <= attr <=100, vous pouvez créer attr une propriété et définir une méthode pour vous assurer que cette condition est vraie sans jamais avoir à changer de code utilisateur.

4
Don O'Donnell

Pour rendre un attribut privé, il suffit de faire self.__attr

class Foo:
    self.__attr = 0

    @property
    def attr(self):
        return self._attr

    @attr.setter
    def attr(self, value):
        self._attr = value

    @attr.deleter
    def attr(self):
        del self._attr
1
Ives Nikiema

En Python, à moins que vous n'ayez besoin d'un comportement spécial d'un attribut, il n'est pas nécessaire de le cacher derrière les méthodes d'accesseur. Si un attribut est destiné à un usage interne uniquement, ajoutez-le avec un trait de soulignement.

0
Chris

La bonne chose à propos des propriétés est qu'elles vous ont donné une interface vraiment cool pour travailler avec. Parfois, il est pratique de dériver une propriété en fonction d'une autre (par exemple, l'IMC est défini par le poids et la taille). L'utilisateur de l'interface n'a bien sûr pas besoin de le savoir.

Je préfère cette façon plutôt que d'avoir des getters et setters explicites comme dans Java ie. Beaucoup plus agréable. :)

0
Juho Vepsäläinen