Question

Supposons que j'ai une classe avec certains attributs. Quel est le meilleur sens (au sens de Pythonic-OOP) pour accéder à ces attributs? Tout comme obj.attr ? Ou peut-être écrire obtenir des accesseurs? Quels sont les styles de nommage acceptés pour de telles choses?

Modifier: Pouvez-vous élaborer sur les meilleures pratiques en matière d'attribution de noms d'attributs avec un trait de soulignement simple ou double? Je constate dans la plupart des modules qu’un seul trait de soulignement est utilisé.

Si cette question a déjà été posée (et j'en ai l'intuition, bien que la recherche n'ait pas donné de résultats), veuillez l'indiquer - et je fermerai celle-ci.

Était-ce utile?

La solution

La façon généralement acceptée de faire les choses consiste à utiliser de simples attributs, 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, vous devez rechercher les "propriétés de la classe python". et l'article de Ryan Tomayko sur Getters / Setters / Fuxors est un bon endroit pour commencer (bien qu'un peu long)

Autres conseils

En ce qui concerne les soulignements 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 autre chose) ne fait pas partie de l'API publique de l'objet. Les gens sauront que le toucher directement, c'est inviter le désastre.

De plus, les attributs de soulignement double interligne (mais pas les attributs de soulignement simples) sont mutilés de noms pour pouvoir y accéder par accident à partir de sous-classes ou n'importe où ailleurs en dehors de la classe actuelle 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 facilement accéder à a._single et à b._single et obtenir l'attribut _single créé par ClassA :

>>> a._single, b._single
('Single', 'Single')
>>> a.getSingle(), b.getSingle(), b.getSingle_B()
('Single', 'Single', 'Single')

Mais essayer d'accéder à l'attribut __ double de l'instance a ou b ne fonctionnera pas directement:

>>> 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 dans un cas):

>>> a.getDouble(), b.getDouble()
('Double', 'Double')

Les méthodes définies sur ClassB ne peuvent 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 d'attribut __ double , lorsqu'il est accédé dans une classe, est mutilé pour inclure le nom de la classe à laquelle on accède dans . Lorsque ClassA tente d'accéder à self .__ double , il se transforme en réalité - lors de compiletime - en un accès self._ClassA__double , et pareillement pour ClassB . (Si une méthode de ClassB devait être affectée à __ double , non inclus dans le code par souci de brièveté, elle ne toucherait donc pas ClassA code> __ double mais créez un nouvel attribut.) Il n’existe aucune autre protection pour cet attribut. Vous pouvez donc 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 lorsque vous souhaitez hériter et modifier le comportement de tout code traitant de cet attribut. Vous devez soit réimplémenter directement tout ce qui touche cet attribut, soit vous devez deviner le nom de la classe et modifier le nom manuellement. Le problème s'aggrave lorsque cet attribut double-underscore est en fait une méthode: redéfinir la méthode ou appeler la méthode dans une sous-classe signifie effectuer manuellement la gestion du nom ou réimplémenter tout le code qui appelle la méthode. n'utilisez pas le nom du double soulignement. Sans parler de l’accès dynamique à l’attribut, avec getattr () : vous devrez également y modifier manuellement.

D'autre part, l'attribut n'étant réécrit que de manière triviale, il n'offre qu'une "protection" superficielle. Tout morceau de code peut toujours accéder à l'attribut manuellement, bien que cela rende leur code dépendant du nom de votre classe, et des efforts que vous déploierez pour reformuler votre coder ou renommer votre classe (tout en conservant le même nom visible par l'utilisateur, une pratique courante en Python) romprait inutilement leur code. Ils peuvent également «piéger» Python pour qu'il modifie leur nom en nommant leur classe de la même façon que la vôtre: remarquez qu'aucun nom de module n'est inclus dans le nom de l'attribut mutilé. Enfin, l’attribut double-underscore est toujours visible dans toutes les listes d’attributs et toutes les formes d’introspection ne prenant pas soin de sauter les attributs commençant par un ( simple ).

Ainsi, si vous utilisez des noms de double soulignement, utilisez-les avec parcimonie, car ils peuvent s'avérer très gênants et ne les utilisez jamais pour des méthodes ou autre qu'une sous-classe peut souhaiter pour réimplémenter, remplacer ou accéder directement . Et réalisez que le double soulignement des noms de soulignement n'offre aucune protection réelle . En fin de compte, l’utilisation d’un seul trait de soulignement vous gagne tout autant et vous apporte moins de douleur (potentielle, future). Utilisez un seul trait de soulignement.

  

Éditer: Pouvez-vous élaborer sur les meilleures pratiques en matière d'attribution de noms d'attributs avec un trait de soulignement simple ou double? Je constate dans la plupart des modules qu’un seul trait de soulignement est utilisé.

Le trait de soulignement simple ne signifie rien de spécial pour Python, c’est la meilleure pratique, qui consiste à dire "hé, vous ne voulez probablement pas y accéder à moins que vous ne sachiez ce que vous faites". Le double soulignement fait toutefois que python mangle le nom en interne, le rendant accessible uniquement à partir de la classe où il est défini.

Le trait de soulignement fin et fin à la fin d'une lettre AND indique une fonction spéciale, telle que __ add __ , appelée lors de l'utilisation de l'opérateur +.

Pour en savoir plus, consultez la PEP 8 , en particulier les "Conventions de nommage". ; section.

Je pense que la plupart des utilisateurs 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 sous-jacentes principales, " __ " sont utilisés pour rendre un attribut ou une fonction privé. Pour les autres conventions, voir PEP 08: http://www.python.org/dev/peps/pep-0008/

Python n'a pas besoin de définir des accesseurs dès le début, car la conversion d'attributs en propriétés est rapide et sans douleur. Voir ce qui suit pour une démonstration éclatante:

Récupération de la dépendance

Il n'y a aucun intérêt à faire des getter / setters en python, vous ne pouvez pas protéger les fichiers de toute façon et si vous avez besoin d'exécuter du code supplémentaire pour obtenir / définir la propriété, regardez la propriété () builtin (python -c ' aide (propriété) ')

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 définir vos attributs en lecture seule ou les affecter uniquement. C'est un peu difficile à faire. L’une des méthodes consiste à remplacer le

> __getattr__

et

> __setattr__

méthodes.

Modifier:

Bien que ma réponse soit toujours vraie, ce n’est pas correct, comme je m’en suis rendu compte. Il existe de meilleurs moyens de créer des accesseurs en python et ils ne sont pas très gênants.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top