attribut" public "ou" privé " en Python? Quelle est la meilleure façon de faire?
en Python, j'ai la classe d'exemple suivante:
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 attribut "_attr" et une propriété pour y accéder. Il y a beaucoup de codes pour déclarer un attribut privé simple et je pense qu'il ne respecte pas la philosophie "baiser" de déclarer tous les attributs comme cela.
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 de l'encapsulation (OOP) dit le contraire!
Quelle est la meilleure façon de faire ?
9 réponses
typiquement, le code Python s'efforce d'adhérer au Principe D'Accès Uniforme. Plus précisément, l'approche acceptée est la suivante:
- Exposez vos variables d'instance directement, permettant, par exemple,
foo.x = 0
, pasfoo.set_x(0)
- si vous devez envelopper les accès à l'intérieur des méthodes, pour quelque raison que ce soit, utilisez
@property
, qui préserve la sémantique de l'accès. C'est-à -foo.x = 0
appellefoo.set_x(0)
.
Le principal avantage de cette approche c'est que l'appelant obtient pour ce faire:
foo.x += 1
même si le code pourrait vraiment être en train de faire:
foo.set_x(foo.get_x() + 1)
le premier énoncé est infiniment plus lisible. Pourtant, avec des 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 aussi que les variables d'instance commençant par un simple underscore sont conventionnellement privé. Qui est, le trait de soulignement des signaux à d'autres développeurs que vous considérez que la valeur est privée, et qu'ils ne devraient pas s'en mêler directement; cependant, rien dans la langue empêche de jouer avec elle directement.
si vous utilisez un underscore à double interligne (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 une sorte de ... plus opaque. Et il y a des arguments valables contre l'utilisation du double d'une part, cela peut rendre le débogage plus difficile.
Le "dsous" (trait de soulignement double, __
) préfixe empêche l'accès à l'attribut, sauf via les 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.
tout simplement, les principes de L'OOP sont erronés. Pourquoi il s'agit d'une longue discussion qui mène à flamewars et est probablement hors sujet pour ce site. : -)
en 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. Donc, ne pas. Facile! :)
vient alors la question: devriez-vous avoir un underscore principal ou non. Et dans l'exemple que vous avez ici, vous devriez certainement pas. Un underscore de premier plan en Python est une convention pour montrer c'est quelque chose interne, et ne font pas partie de l'API, et que vous devez l'utiliser sur vos propres risques. Ce n'est évidemment pas le cas ici, mais c'est une convention commune et utile.
Python n'a pas d'attributs publics ou privés. Tous les attributs sont accessibles à tous les codes.
self.attr = 0 #Done
votre méthode ne rend pas _attr privé, c'est juste un peu de confusion.
comme d'autres l'ont dit, les attributs privés en Python sont simplement une convention. L'utilisation de property
la syntaxe doit être utilisée pour le traitement spécial lorsque les attributs sont liés, modifiés ou supprimés. La beauté de 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 d'attr pour dire 0 <= attr <=100
, vous pouvez faire attr
un bien et de définir une méthode pour s'assurer que cette condition est vraie, sans jamais devoir changer n'importe quel code utilisateur.
pour rendre un attribut privé, vous avez juste à 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
Voir ce lien:https://docs.python.org/2/tutorial/classes.html
9.6. Variables privées et classe-références locales
les variables d'instance "privées" qui ne peuvent être accédées que de l'intérieur d'un objet n'existent pas en Python. Cependant, il existe une convention qui est suivie par la plupart des codes Python: un nom préfixé avec un caractère de soulignement (par exemple _spam) doit être traité comme une partie non publique de l'API (qu'il s'agisse d'une fonction, d'une méthode ou d'un membre de données). Il doit être considéré comme un détail de mise en œuvre et peut être modifié sans préavis.
comme il existe un cas d'utilisation valable pour les membres de classe-privés (à savoir pour éviter les conflits de noms avec des noms définis par des sous-classes), il y a un soutien limité pour un tel mécanisme, appelé nom mangling. Tout identifiant de la forme _ _ _ spam (au moins deux underscores principaux, au plus un underscore arrière) est textuellement remplacé par _classname _ _ spam, où classname est le nom de classe courant avec trait de soulignement(s) dépouillé. Cette modification est effectuée sans tenir compte de la position syntaxique de l'identificateur, tant qu'elle se produit dans le cadre de la définition d'une classe.
en Python, sauf si vous avez besoin d'un comportement spécial à partir d'un attribut, il n'y a pas besoin de le cacher derrière les méthodes accessor. Si un attribut est réservé à un usage interne, il doit être précédé d'un caractère de soulignement.
ce qui est bien avec les propriétés, c'est qu'elles vous donnent une interface vraiment cool avec laquelle travailler. Il est parfois pratique de dériver une propriété basée sur quelques autres (ie. L'IMC est défini par le poids et la taille). L'utilisateur de l'interface ne doit pas savoir, bien sûr.
je préfère de cette façon que D'avoir des getters et des setters explicites comme en Java ie. De façon plus agréable. :)