Attributs d'objet Python-méthodologie pour l'accès
Supposons que j'ai une classe avec quelques attributs. Comment est-il préférable (dans le sens Pythonic-OOP) d'accéder à ces attributs ? Tout comme obj.attr
? Ou peut-être écrire accesseurs get ?
Quels sont les styles de nommage acceptés pour de telles choses ?
Modifier: Pouvez-vous élaborer sur les meilleures pratiques de nommer les attributs avec un trait de soulignement simple ou double? Je vois dans la plupart des modules qu'un seul trait de soulignement est utilisé.
Si cette question a déjà été posée (et j'ai une intuition il a, bien que la recherche n'a pas apporté de résultats), veuillez le pointer-et je vais fermer celui-ci.
7 réponses
La façon généralement acceptée de faire les choses est simplement d'utiliser des attributs simples, comme ceci
>>> class MyClass:
... myAttribute = 0
...
>>> c = MyClass()
>>> c.myAttribute
0
>>> c.myAttribute = 1
>>> c.myAttribute
1
Si vous avez besoin de pouvoir écrire des getters et des setters, alors ce que vous voulez rechercher est "Python class properties" et L'article de Ryan Tomayko sur Getters / setters / Fuxors {[5] } est un excellent endroit pour commencer (bien qu'un peu long)
En ce qui concerne les traits de soulignement simples et doubles: les deux indiquent le même concept de "caractère privé". C'est-à-dire que les gens sauront que l'attribut (que ce soit une méthode ou un attribut de données "normal" ou toute autre chose) ne fait pas partie de l'API publique de l'objet. Les gens sauront que le toucher directement est d'inviter la catastrophe.
En plus de cela, les attributs de soulignement à double tête (mais pas les attributs de soulignement à simple tête) sont nom-mutilés à créer leur accès par accident à partir de sous-classes ou ailleurs en dehors de la classe actuelle est moins probable. Vous pouvez toujours y accéder, mais pas aussi trivialement. Par exemple:
>>> class ClassA:
... def __init__(self):
... self._single = "Single"
... self.__double = "Double"
... def getSingle(self):
... return self._single
... def getDouble(self):
... return self.__double
...
>>> class ClassB(ClassA):
... def getSingle_B(self):
... return self._single
... def getDouble_B(self):
... return self.__double
...
>>> a = ClassA()
>>> b = ClassB()
Vous pouvez maintenant trivialement l'accès a._single
et b._single
et obtenir le _single
attribut créé par ClassA
:
>>> a._single, b._single
('Single', 'Single')
>>> a.getSingle(), b.getSingle(), b.getSingle_B()
('Single', 'Single', 'Single')
Mais essayer d'accéder directement à l'attribut __double
sur l'instance a
ou b
ne fonctionnera pas:
>>> a.__double
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: ClassA instance has no attribute '__double'
>>> b.__double
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: ClassB instance has no attribute '__double'
Et bien que les méthodes définies dans ClassA
puissent y accéder directement (lorsqu'elles sont appelées soit, par exemple:
>>> a.getDouble(), b.getDouble()
('Double', 'Double')
Les Méthodes définies sur ClassB
ne peut pas:
>>> b.getDouble_B()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in getDouble_B
AttributeError: ClassB instance has no attribute '_ClassB__double'
Et juste dans cette erreur, vous obtenez un indice sur ce qui se passe. Le nom de l'attribut __double
, lorsqu'il est accédé à l'intérieur d'une classe, est modifié pour inclure le nom de la classe à laquelle il est accédé dans. Lorsque ClassA
essaie d'accéder à self.__double
, il se transforme en fait-à compiletime-en un accès de self._ClassA__double
, et de même pour ClassB
. (Si une méthode dans ClassB
devait être assignée à __double
, non incluse dans le code par souci de concision, il ne toucherait donc pas ClassA
__double
mais créerait un nouvel attribut.) Il n'y a pas d'autre protection de cet attribut, donc vous pouvez toujours y accéder directement si vous connaissez le bon nom:
>>> a._ClassA__double, b._ClassA__double
('Double', 'Double')
Alors, pourquoi est-ce un problème?
Eh bien, c'est un problème chaque fois que vous voulez hériter et changer le comportement de tout code traitant de cet attribut. Vous devez soit réimplémenter tout ce qui touche directement cet attribut à double trait de soulignement, soit vous devez deviner le nom de la classe et le modifier manuellement. Le problème s'aggrave lorsque cet attribut double-underscore est en fait une méthode: remplacer la méthode ou appeler la méthode dans une sous-classe signifie faire le nom manuellement, ou réimplémenter tout le code qui appelle la méthode pour ne pas utiliser le nom double-underscore. Sans parler de l'accès dynamique à l'attribut, avec getattr()
: vous devrez également y manipuler manuellement.
D'autre part, parce que l'attribut n'est réécrit que de manière triviale, il n'offre qu'une "protection" superficielle. Tout morceau de code peut toujours obtenir l'attribut en mangeant manuellement, bien que cela rende leur code dépendant du nom de votre classe, et les efforts de votre côté pour refactoriser votre code ou renommer votre classe (tout en gardant le même nom visible par l'utilisateur, une pratique courante en Python) briserait inutilement leur code. Ils peuvent aussi "tromper" Python en faisant le nom-mangling pour eux en nommant leur classe comme la vôtre: remarquez qu'il n'y a pas de nom de module inclus dans le nom d'attribut mutilé. Et enfin, l'attribut double-underscore est toujours visible dans toutes les listes d'attributs et toutes les formes d'introspection qui ne prennent pas soin d'ignorer les attributs commençant par un trait de soulignement (single).
Donc, si vous utilisez des noms à double trait de soulignement, utilisez-les avec parcimonie, car ils peuvent s'avérer très gênants et ne jamais les utiliser pour les méthodes ou toute autre chose qu'une sous-classe peut vouloir réimplémenter, remplacer ou accéder directement . Et se rendre compte que le nom de soulignement double-tête-mangling offre aucune protection réelle . En fin de Compte, l'utilisation d'un seul trait de soulignement vous gagne tout autant et vous donne moins de douleur (potentielle, future). L'utilisation d'un seul trait de soulignement.
Edit: pouvez-vous élaborer sur les meilleures pratiques de nommer les attributs avec un trait de soulignement simple ou double? Je vois dans la plupart des modules qu'un seul trait de soulignement est utilisé.
Un seul trait de soulignement ne signifie rien de spécial pour python, c'est juste la meilleure pratique, de dire "hey vous ne voulez probablement pas y accéder à moins que vous ne sachiez ce que vous faites". Double underscore cependant rend Python mangle le nom en interne le rendant accessible uniquement à partir de la classe où il se trouve définir.
Le double trait de soulignement de début et de fin indique une fonction spéciale, telle que __add__
qui est appelée lors de l'utilisation de l'opérateur+.
En savoir plus dans PEP 8 , en particulier la section "Conventions de nommage".
Je pense que la plupart y accèdent directement, pas besoin de méthodes get / set.
>>> class myclass:
... x = 'hello'
...
>>>
>>> class_inst = myclass()
>>> class_inst.x
'hello'
>>> class_inst.x = 'world'
>>> class_inst.x
'world'
BTW, vous pouvez utiliser la fonction dir() pour voir quels attributs / méthodes sont attachés à votre instance:
>>> dir(class_inst)
['__doc__', '__module__', 'x']
Deux barres inférieures,"__", sont utilisées pour rendre privé un attribut ou une fonction. Pour les autres conventions voir PEP 08: http://www.python.org/dev/peps/pep-0008/
Python n'a pas besoin de définir les accesseurs dès le début, car la conversion des attributs en propriétés est rapide et indolore. Voir ce qui suit pour une démonstration vivante:
Il n'y a pas vraiment de point de faire getter / setters en python, vous ne pouvez pas protéger les choses de toute façon et si vous avez besoin d'exécuter du code supplémentaire lors de l'obtention/La définition de la propriété, regardez la propriété() builtin (Python-C 'help (property)')
Certaines personnes utilisent des getters et des setters. Selon le style de codage que vous utilisez, vous pouvez les nommer getSpam et seteggs. Mais vous pouvez également vous faire des attributs en lecture seule ou affecter uniquement. C'est un peu difficile à faire. Une façon est de remplacer le
> __getattr__
Et
> __setattr__
Méthodes.
Modifier:
Bien que ma réponse soit toujours vraie, ce n'est pas juste, comme je l'ai compris. Il existe de meilleures façons de faire des accesseurs en python et ne sont pas très gênants.