web-dev-qa-db-fra.com

Définir des fonctions de module privé en python

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

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

  • Privé fonctions, qui ne peuvent pas être appelées à partir de en dehors de leur module

Cependant, si je définis deux fichiers:

#a.py
__num=1

et:

#b.py
import a
print a.__num

quand je lance b.py, il affiche 1 sans donner aucune exception. Est-ce que diveintopython est faux ou ai-je mal compris quelque chose? Et y a-t-il un moyen de do définir la fonction d'un module comme étant privée?

192
olamundo

En Python, la "confidentialité" dépend des niveaux d'accord des "adultes consentants" - vous ne pouvez pas forcer (pas plus que vous ne pouvez en réalité) ;-) Un trait de soulignement simple signifie que vous n'êtes pas supposé y accéder "de l'extérieur" - deux les soulignements principaux (sans soulignements à la fin) transmettent le message avec encore plus de force ... mais, au bout du compte, cela dépend toujours des conventions sociales et du consensus: l'introspection de Python est suffisamment puissante pour que vous ne puissiez pas menotter tous les autres programmeurs du monde respectent vos souhaits.

((Btw, bien que ce soit un secret bien gardé, il en va de même pour C++: avec la plupart des compilateurs, une simple ligne #define private public devant #include et votre fichier .h suffit aux codeurs astucieux faire du hasch de votre "vie privée" ...! -))

284
Alex Martelli

Il peut y avoir une confusion entre membres privés de la classe et membres privés du module.

Un module private _ commence par un trait de soulignement
Un tel élément n'est pas copié avec la forme from <module_name> import * de la commande d'importation; il est toutefois importé si vous utilisez la syntaxe import <moudule_name> ( voir la réponse de Ben Wilhelm )
Supprimez simplement un trait de soulignement du nombre a .__ de l’exemple de la question. Les modules qui importent a.py n’afficheront pas ce caractère en utilisant la syntaxe from a import *.

Un classe privée commence par deux traits de soulignement (alias dunder, c.-à-d. Sous-score d-ouble) 
.__ Une telle variable a son nom "mutilé" pour inclure le nom de classe, etc.
On peut toujours y accéder en dehors de la logique de classe, via le nom mutilé.
Bien que le changement de nom puisse servir de dispositif de prévention légère 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 à 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
>>>
244
mjv

La réponse à cette question n'a pas été complète, car la confidentialité des modules n'est pas purement conventionnelle et l'utilisation de import peut ou non reconnaître la confidentialité des modules, selon l'utilisation qui en est faite.

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

#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 les fichiers non privés 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 est préférable d’être explicite lors de l’importation de noms depuis des modules, plutôt que de tous les importer avec un '*':

#b.py
from a import name1 
from a import name2
...
72
Ben Wilhelm

Python autorise les membres privés class avec le préfixe de soulignement double. Cette technique ne fonctionne pas au niveau du module, je pense donc 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
29
Andrew Hare

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é.

5
Ilian Zapryanov

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

Le didacticiel Python » Les classes » Variables privées

1
user1338062

intégré avec des fermetures ou des fonctions est un moyen. Ceci est courant dans JS bien que cela ne soit pas nécessaire pour les plates-formes autres que les navigateurs ni pour les utilisateurs de navigateurs.

En Python, cela semble un peu étrange, mais si quelque chose a vraiment besoin d'être caché, cela pourrait être la solution. Plus précisément, utiliser l'API python et conserver les éléments devant être cachés dans le C (ou un autre langage) est probablement la meilleure solution. Faute de quoi, j'insérerais le code dans une fonction, l'appelant et le renvoyant aux éléments que vous souhaitez exporter.

0
Martian Fame