Classe «privée» (implémentation) en Python

107

Je suis en train de coder un petit module Python composé de deux parties:

  • certaines fonctions définissant une interface publique,
  • une classe d'implémentation utilisée par les fonctions ci-dessus, mais qui n'a pas de sens en dehors du module.

Au début, j'ai décidé de «cacher» cette classe d'implémentation en la définissant dans la fonction qui l'utilise, mais cela entrave la lisibilité et ne peut pas être utilisée si plusieurs fonctions réutilisent la même classe.

Donc, en plus des commentaires et des docstrings, existe-t-il un mécanisme pour marquer une classe comme "privée" ou "interne"? Je connais le mécanisme de soulignement, mais si je comprends bien, il ne s'applique qu'aux variables, aux fonctions et aux noms de méthodes.

oparisie
la source

Réponses:

175

Utilisez un seul préfixe de soulignement:

class _Internal:
    ...

C'est la convention officielle Python pour les symboles «internes»; "from module import *" n'importe pas les objets avec un préfixe de soulignement.

Edit: Référence à la convention de soulignement unique

Ferdinand Beyer
la source
3
Je ne connaissais pas la règle de soulignement étendue aux classes. Je ne veux pas encombrer mon espace de noms lors de l'importation, donc ce comportement est ce que je recherchais. Merci!
oparisy le
1
Puisque vous déclarez qu'il s'agit de la convention python "officielle", ce serait bien avec un lien. Un autre article ici dit que tout est de la manière officielle et fait un lien vers la documentation.
flodin le
11
python.org/dev/peps/pep-0008 - _single_leading_underscore: faible indicateur "d'utilisation interne". Par exemple, "from M import *" n'importe pas les objets dont le nom commence par un trait de soulignement. - Les classes à usage interne ont un trait de soulignement principal
Miles
2
Un trait de soulignement principal est la convention pour marquer les choses comme internes, "ne vous embêtez pas avec ça", alors que tout est plus pour les modules conçus pour être utilisés avec "from M import *", sans nécessairement impliquer que les utilisateurs du module ne doivent pas toucher cette classe.
Miles
65

En bref:

  1. Vous ne pouvez pas appliquer la confidentialité . Il n'y a pas de classes / méthodes / fonctions privées en Python. Au moins, pas de confidentialité stricte comme dans d'autres langages, tels que Java.

  2. Vous ne pouvez indiquer / suggérer la confidentialité . Cela suit une convention. La convention python pour marquer une classe / fonction / méthode comme privée est de la faire précéder d'un _ (trait de soulignement). Par exemple, def _myfunc()ou class _MyClass:. Vous pouvez également créer une pseudo-confidentialité en faisant précéder la méthode de deux traits de soulignement (par exemple:) __foo. Vous ne pouvez pas accéder directement à la méthode, mais vous pouvez toujours l'appeler via un préfixe spécial en utilisant le nom de classe (par exemple:) _classname__foo. Donc, le mieux que vous puissiez faire est d'indiquer / suggérer la confidentialité, et non de la faire respecter.

Python est comme Perl à cet égard. Pour paraphraser une ligne célèbre sur la vie privée du livre Perl, la philosophie est que vous devriez rester en dehors du salon parce que vous n'avez pas été invité, pas parce qu'il est défendu avec un fusil de chasse.

Pour plus d'informations:

Karl Fast
la source
En réponse au n ° 1, vous pouvez en quelque sorte appliquer la confidentialité des méthodes. L'utilisation d'un double trait de soulignement comme __method (self) le rendra inaccessible en dehors de la classe. Mais il y a un moyen de contourner cela en l'appelant comme, Foo () ._ Foo__method (). Je suppose que cela le renomme simplement en quelque chose de plus ésotérique.
Evan Fosmark
1
Cette citation est exacte.
Paul Draper
37

Définissez __all__, une liste de noms que vous souhaitez exporter ( voir documentation ).

__all__ = ['public_class'] # don't add here the 'implementation_class'
OncleZeiv
la source
10

Un modèle que j'utilise parfois est le suivant:

Définissez une classe:

class x(object):
    def doThis(self):
        ...
    def doThat(self):
        ...

Créez une instance de la classe en écrasant le nom de la classe:

x = x()

Définissez des symboles qui exposent la fonctionnalité:

doThis = x.doThis
doThat = x.doThat

Supprimez l'instance elle-même:

del x

Vous avez maintenant un module qui expose uniquement vos fonctions publiques.

theller
la source
2
Il a fallu une minute pour comprendre le but / la fonction de "remplacer le nom de la classe", mais j'ai eu un grand sourire quand je l'ai fait. Je ne sais pas quand je vais l'utiliser. :)
Zach Young
Y a-t-il un nom pour cette technique?
Bishwas Mishra
8

La convention est précédée de "_" aux classes, fonctions et variables internes.

Benjamin Peterson
la source
4

Pour aborder la question des conventions de conception, et comme l'a dit Christopher, il n'y a vraiment rien de tel que «privé» en Python. Cela peut sembler tordu pour quelqu'un qui vient de fond C / C ++ (comme moi il y a quelque temps), mais finalement, vous réaliserez probablement que les conventions suivantes sont suffisantes.

Voir quelque chose avec un trait de soulignement devant devrait être un indice suffisant pour ne pas l'utiliser directement. Si vous êtes préoccupé par l'encombrement de la help(MyClass)sortie (ce que tout le monde regarde lors de la recherche sur la façon d'utiliser une classe), les attributs / classes soulignés ne sont pas inclus ici, donc vous finirez par avoir simplement votre interface "publique" décrite.

De plus, avoir tout ce qui est public a ses propres avantages, comme par exemple, vous pouvez tester à peu près n'importe quoi de l'extérieur (ce que vous ne pouvez pas vraiment faire avec les constructions privées C / C ++).

Dimitri Tcaciuc
la source
4

Utilisez deux traits de soulignement pour préfixer les noms d'identificateurs «privés». Pour les classes d'un module, utilisez un seul trait de soulignement et elles ne seront pas importées à l'aide de "from module import *".

class _MyInternalClass:
    def __my_private_method:
        pass

(Il n'y a pas de véritable "privé" en Python. Par exemple, Python modifie automatiquement les noms des membres de la classe avec des doubles traits de soulignement __clssname_mymember. Donc, vraiment, si vous connaissez le nom mutilé, vous pouvez quand même utiliser l'entité "privée" . Voir ici. Et bien sûr, vous pouvez choisir d'importer manuellement les classes "internes" si vous le souhaitez).

chroder
la source
Pourquoi deux _? Un suffit.
S.Lott le
Un seul trait de soulignement suffit pour empêcher "import" d'importer des classes et des fonctions. Vous avez besoin de deux traits de soulignement pour induire la fonction de modification des noms de Python. Aurait dû être plus clair; J'ai édité.
chroder
Maintenant, le suivi. Pourquoi nommer la mutilation? Quel est l'avantage possible de cela?
S.Lott
5
L'avantage possible est d'ennuyer les autres développeurs qui ont besoin d'accéder à ces variables :)
Richard Levasseur