Définition de fonctions de module privé en python

238

Selon http://www.faqs.org/docs/diveintopython/fileinfo_private.html :

Comme la plupart des langages, Python a le concept d'éléments privés:

  • Fonctions privées, qui ne peuvent pas être appelées depuis l' extérieur de leur module

Cependant, si je définis deux fichiers:

#a.py
__num=1

et:

#b.py
import a
print a.__num

quand je l'exécute, b.pyil imprime 1sans donner aucune exception. Diveintopython est-il mauvais, ou ai-je mal compris quelque chose? Et est - il possible de ne pas définir la fonction d'un module en privé?

olamundo
la source
Ce n'est pas que diveintopython est faux, mais dans leur exemple: >>> import fileinfo >>> m = fileinfo.MP3FileInfo() >>> m.__parse("/music/_singles/kairo.mp3") 1 Traceback (innermost last): File "<interactive input>", line 1, in ? AttributeError: 'MP3FileInfo' instance has no attribute '__parse' fileinfo.MP3FileInfo () est une instance de classe. Ce qui donne cette exception lorsque vous utilisez un double soulignement. Alors que dans votre cas, vous n'avez pas créé de classe, vous venez de créer un module. Voir aussi: stackoverflow.com/questions/70528/…
Homero Esmeraldo

Réponses:

324

En Python, la "confidentialité" dépend des niveaux d'accord des "adultes consentants" - vous ne pouvez pas la forcer (pas plus que vous ne le pouvez dans la vie réelle ;-). Un seul trait de soulignement principal signifie que vous n'êtes pas censé y accéder "de l'extérieur" - deux traits de soulignement principaux (sans traits de soulignement finaux) véhiculent le message encore plus avec force ... mais, en fin de compte, cela dépend toujours des réseaux sociaux. convention et consensus: l'introspection de Python est suffisamment puissante pour que vous ne puissiez pas menotter tous les autres programmeurs du monde pour respecter vos souhaits.

((Btw, bien que ce soit un secret bien gardé, il en va de même pour le C ++: avec la plupart des compilateurs, une simple #define private publicligne avant de #includesaisir votre .hfichier est tout ce qu'il faut pour que les codeurs rusés hachent votre "confidentialité" ...! -)) )

Alex Martelli
la source
82
Votre note sur C ++ est incorrecte. En utilisant #define private public, vous modifiez le code qui est envoyé au compilateur, où se trouve le changement de nom.
rhinoinrepose
14
De plus, le mangling C ++ est obscur, mais à peine secret. Vous pouvez également "introspecter" un binaire produit par C ++. OT, désolé.
Prof.Falken
47
En tant que mise à jour de @rhinoinrepose, ce n'est pas seulement incorrect, c'est un comportement non défini selon la norme de redéfinir un mot clé avec une macro de préprocesseur.
Cory Kramer
3
@AlexMartelli n'est pas static void foo()aussi privé que possible. Il est au moins masqué par l'éditeur de liens et la fonction peut être supprimée entièrement par inline.
user877329
3
Dans la vraie vie, les gens sont poursuivis s'ils enfreignent les lois
Lay González
289

Il peut y avoir confusion entre les classes privées et les modules privés .

Un module privé commence par un trait de soulignement
Un tel élément n'est pas copié lors de l'utilisation du from <module_name> import *formulaire de la commande d'importation; il est cependant importé si vous utilisez la import <moudule_name>syntaxe ( voir la réponse de Ben Wilhelm )
Supprimez simplement un trait de soulignement du numéro a .__ de l'exemple de la question et il ne s'affichera pas dans les modules qui importent a.py en utilisant la from a import *syntaxe.

Une classe privée commence par deux traits de soulignement (alias dunder ie d-ouble under-score)
Une telle variable a son nom "mangled" pour inclure le nom de classe etc.
Elle peut toujours être consultée en dehors de la logique de la classe, via le nom mangled.
Bien que le changement de nom puisse servir de moyen de prévention léger contre les accès non autorisés, son objectif principal est d'empêcher d'éventuelles collisions de noms avec les membres de la classe des classes ancêtres. Voir la référence amusante mais précise d'Alex Martelli aux adultes consentants alors qu'il décrit la convention utilisée en ce qui concerne ces variables.

>>> class Foo(object):
...    __bar = 99
...    def PrintBar(self):
...        print(self.__bar)
...
>>> myFoo = Foo()
>>> myFoo.__bar  #direct attempt no go
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute '__bar'
>>> myFoo.PrintBar()  # the class itself of course can access it
99
>>> dir(Foo)    # yet can see it
['PrintBar', '_Foo__bar', '__class__', '__delattr__', '__dict__', '__doc__', '__
format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__
', '__subclasshook__', '__weakref__']
>>> myFoo._Foo__bar  #and get to it by its mangled name !  (but I shouldn't!!!)
99
>>>
mjv
la source
Eh bien, TIL. __private_functionMais pourquoi ne pas appliquer le niveau module ? Je suis tombé sur cela et j'ai eu des erreurs à cause de cela.
Santa
Merci pour les mots aimables @Terrabits, mais je me tiens volontiers au deuxième rang derrière Alex (bien derrière!) Sur tout ce qui concerne 'Python'. De plus, ses réponses sont généralement plus concises et pleines d'humour tout en conservant un haut niveau d'autorité, étant donné les contributions importantes d'Alex à la langue et à la communauté.
mjv
1
@mjv C'était une explication tellement utile! Je vous remercie! J'ai été assez perplexe à propos de ce comportement pendant un certain temps. Je souhaite que le choix ait été de jeter une sorte d'erreur autre qu'un AttributeError si vous avez essayé d'accéder directement à la classe private; peut-être une "PrivateAccessError" ou quelque chose aurait été plus explicite / utile. (Étant donné que l'erreur qu'il n'a pas d'attribut n'est pas vraiment vraie).
HFBrowning
82

Cette question n'a pas reçu de réponse complète, car la confidentialité des modules n'est pas purement conventionnelle, et puisque l'utilisation de l' importation peut ou non reconnaître la confidentialité des modules, selon la façon dont elle est utilisée.

Si vous définissez des noms privés dans un module, ces noms seront importés dans tout script utilisant la syntaxe «import module_name». Ainsi, en supposant que vous avez correctement défini dans votre exemple le module private, _num, dans a.py, comme ça.

#a.py
_num=1

..vous pourrez y accéder dans b.py avec le symbole du nom du module:

#b.py
import a
...
foo = a._num # 1

Pour importer uniquement des utilisateurs non privés à partir de a.py, vous devez utiliser la syntaxe from :

#b.py
from a import *
...
foo = _num # throws NameError: name '_num' is not defined

Par souci de clarté, cependant, il vaut mieux être explicite lors de l'importation de noms à partir de modules, plutôt que de les importer tous avec un '*':

#b.py
from a import name1 
from a import name2
...
Ben Wilhelm
la source
1
où spécifiez-vous les fonctions / bibliothèques à importer? dans l' init .py?
FistOfFury
Il n'y a aucun risque de collision de noms lorsqu'ils _namessont invoqués avec import a- ce sont des accès comme a._nameslors de l'utilisation de ce style.
Josiah Yoder
@FistOfFury Oui, vous spécifiez les fonctions importées dans le __init__.pyfichier. Voir ici pour de l'aide à ce sujet.
Mike Williamson
29

Python permet aux membres de classe privés avec le préfixe de soulignement double. Cette technique ne fonctionne pas au niveau du module, donc je pense que c'est une erreur dans Dive Into Python.

Voici un exemple de fonctions de classe privées:

class foo():
    def bar(self): pass
    def __bar(self): pass

f = foo()
f.bar()   # this call succeeds
f.__bar() # this call fails
Andrew Hare
la source
2
Je pense que l'intention du PO est d'écrire des fonctions qui ne sont pas accessibles en dehors, par exemple, d'un package commercial. À cet égard, cette réponse n'est pas complète. La fonction __bar () est toujours accessible de l'extérieur via f._foo__bar (). Par conséquent, les traits de soulignement à double interligne ne le rendent pas privé.
SevakPrime
24

Vous pouvez ajouter une fonction interne:

def public(self, args):
   def private(self.root, data):
       if (self.root != None):
          pass #do something with data

Quelque chose comme ça si vous avez vraiment besoin de ce niveau de confidentialité.

Ilian Zapryanov
la source
9
Pourquoi n'est-ce pas la meilleure réponse?
safay
2

C'est une question ancienne, mais les variables mutilées privées du module (un trait de soulignement) et privées de classe (deux traits de soulignement) sont désormais couvertes dans la documentation standard:

Le didacticiel Python » Classes » Variables privées

user1338062
la source
1

intégré avec des fermetures ou des fonctions est un moyen. Ceci est courant dans JS bien qu'il ne soit pas requis pour les plates-formes autres que les navigateurs ou les navigateurs.

En Python, cela semble un peu étrange, mais si quelque chose doit vraiment être caché, cela pourrait être le cas. Plus précisément, utiliser l'API python et garder les choses qui doivent être cachées dans le C (ou un autre langage) est probablement la meilleure façon. À défaut, j'irais mettre le code dans une fonction, l'appeler et lui faire retourner les éléments que vous souhaitez exporter.

La renommée martienne
la source
-11

Python a trois modes via., Privé, public et protégé. Lors de l'importation d'un module, seul le mode public est accessible. Les modules privés et protégés ne peuvent donc pas être appelés de l'extérieur du module, c'est-à-dire lors de son importation.

sravan
la source