web-dev-qa-db-fra.com

Pourquoi les méthodes "privées" de Python ne sont-elles pas réellement privées?

Python nous donne la possibilité de créer des méthodes et des variables "privées" dans une classe en ajoutant un double soulignement au nom, comme ceci: __myPrivateMethod(). Comment, alors, peut-on expliquer cela

>>> class MyClass:
...     def myPublicMethod(self):
...             print 'public method'
...     def __myPrivateMethod(self):
...             print 'this is private!!'
... 
>>> obj = MyClass()
>>> obj.myPublicMethod()
public method
>>> obj.__myPrivateMethod()
Traceback (most recent call last):
  File "", line 1, in 
AttributeError: MyClass instance has no attribute '__myPrivateMethod'
>>> dir(obj)
['_MyClass__myPrivateMethod', '__doc__', '__module__', 'myPublicMethod']
>>> obj._MyClass__myPrivateMethod()
this is private!!

Quel est le problème?!

Je vais expliquer cela un peu pour ceux qui ne l'ont pas bien compris.

>>> class MyClass:
...     def myPublicMethod(self):
...             print 'public method'
...     def __myPrivateMethod(self):
...             print 'this is private!!'
... 
>>> obj = MyClass()

Ce que j'ai fait là-bas est de créer une classe avec une méthode publique et une méthode privée et de l'instancier.

Ensuite, j'appelle sa méthode publique.

>>> obj.myPublicMethod()
public method

Ensuite, j'essaie d'appeler sa méthode privée.

>>> obj.__myPrivateMethod()
Traceback (most recent call last):
  File "", line 1, in 
AttributeError: MyClass instance has no attribute '__myPrivateMethod'

Tout a l'air bien ici; nous ne pouvons pas l'appeler. C'est en fait 'privé'. En fait, ce n'est pas le cas. Lancer dir () sur l'objet révèle une nouvelle méthode magique que python crée de manière magique pour toutes vos méthodes 'privées'.

>>> dir(obj)
['_MyClass__myPrivateMethod', '__doc__', '__module__', 'myPublicMethod']

Le nom de cette nouvelle méthode est toujours un trait de soulignement, suivi du nom de la classe, suivi du nom de la méthode.

>>> obj._MyClass__myPrivateMethod()
this is private!!

Voilà pour l'encapsulation, hein?

Dans tous les cas, j'avais toujours entendu dire que Python ne supportait pas l'encapsulation, alors pourquoi même essayer? Ce qui donne?

557
willurd

Le brouillage de noms permet de garantir que les sous-classes ne remplacent pas accidentellement les méthodes et attributs privés de leurs super-classes. Il n'est pas conçu pour empêcher un accès délibéré de l'extérieur.

Par exemple:

>>> class Foo(object):
...     def __init__(self):
...         self.__baz = 42
...     def foo(self):
...         print self.__baz
...     
>>> class Bar(Foo):
...     def __init__(self):
...         super(Bar, self).__init__()
...         self.__baz = 21
...     def bar(self):
...         print self.__baz
...
>>> x = Bar()
>>> x.foo()
42
>>> x.bar()
21
>>> print x.__dict__
{'_Bar__baz': 21, '_Foo__baz': 42}

Bien sûr, cela tombe en panne si deux classes différentes portent le même nom.

524
Alya

Exemple de fonction privée

import re
import inspect

class MyClass :

    def __init__(self) :
        pass

    def private_function ( self ) :
        try :
            function_call = inspect.stack()[1][4][0].strip()

            # See if the function_call has "self." in the begining
            matched = re.match( '^self\.', function_call )
            if not matched :
                print 'This is Private Function, Go Away'
                return
        except :
            print 'This is Private Function, Go Away'
            return

        # This is the real Function, only accessible inside class #
        print 'Hey, Welcome in to function'

    def public_function ( self ) :
        # i can call private function from inside the class
        self.private_function()

### End ###
206
arun

Quand je suis arrivé de Java à Python pour la première fois, je détestais ceci. Cela m'a fait peur à mort.

Aujourd'hui, c'est peut-être la seule chose que {j'aime le plus} à propos de Python.

J'aime être sur une plate-forme, où les gens se font confiance et ne ressentent pas le besoin de construire des murs impénétrables autour de leur code. Dans les langages fortement encapsulés, si une API comporte un bogue et que vous avez compris ce qui ne va pas, vous ne pourrez toujours pas le contourner car la méthode nécessaire est privée. En Python, l'attitude est: "bien sûr". Si vous pensez que vous comprenez la situation, vous l’avez peut-être même lue, alors tout ce que nous pouvons dire, c’est "bonne chance!".

Rappelez-vous que l’encapsulation n’a pas même un lien faible avec la "sécurité", ou de garder les enfants hors de la pelouse. C'est juste un autre motif qui devrait être utilisé pour rendre une base de code plus facile à comprendre.

141
Thomas Ahle

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

Strictement parlant, les méthodes privées sont accessibles en dehors de leur classe, juste pas facilement accessible. Rien dedans Python est vraiment privé; intérieurement, les noms des méthodes privées et les attributs sont mutilés et démêlés à la volée pour les faire paraître inaccessibles par leurs prénoms. Vous pouvez accéder à la méthode __parse du fichier Classe MP3FileInfo par le nom _MP3FileInfo__parse. Reconnaissez que c'est intéressant, puis promettez de jamais, jamais le faire dans le code réel . Les méthodes privées sont privées pour un raison, mais comme beaucoup d'autres choses dans Python, leur caractère privé est finalement, une question de convention, pas Obliger.

134
xsl

L'expression couramment utilisée est "nous sommes tous des adultes consentants ici". En ajoutant un simple soulignement (ne pas exposer) ou un double soulignement (masquer), vous indiquez à l'utilisateur de votre classe que vous souhaitez que le membre soit «privé» d'une manière ou d'une autre. Cependant, vous faites confiance à tous les autres pour qu'ils se comportent de manière responsable et respectent ce principe, sauf s'ils ont une raison impérieuse de ne pas le faire (par exemple, les débogueurs, la complétion de code).

Si vous devez vraiment avoir quelque chose de privé, vous pouvez l’implémenter dans une extension (par exemple en C pour CPython). Dans la plupart des cas, cependant, vous apprenez simplement la façon de faire les choses en Pythonic.

86
Tony Meyer

Ce n'est pas comme si vous ne pouviez absolument pas contourner la confidentialité des membres dans toutes les langues (arithmétique de pointeur en C++, Reflections en .NET/Java).

Le fait est que vous obtenez une erreur si vous essayez d'appeler la méthode privée par accident. Mais si vous voulez vous tirer une balle dans le pied, allez-y et faites-le.

Edit: Vous n'essayez pas de sécuriser vos données par OO-encapsulation, n'est-ce pas?

32
Maximilian

Un comportement similaire existe lorsque les noms d'attribut de module commencent par un simple soulignement (par exemple, _foo).

Les attributs de module nommés en tant que tels ne seront pas copiés dans un module d'importation si vous utilisez la méthode from*, par exemple

from bar import *

Cependant, il s’agit d’une convention et non d’une contrainte de langage. Ce ne sont pas des attributs privés; ils peuvent être référencés et manipulés par n'importe quel importateur. Certains avancent que pour cette raison, Python ne peut pas implémenter une véritable encapsulation.

12
Ross

C'est juste un de ces choix de conception de langue. À un certain niveau, ils sont justifiés. Ils font en sorte que vous devez aller assez loin pour essayer d'appeler la méthode, et si vous en avez vraiment besoin, vous devez avoir une très bonne raison!

Le débogage et les tests me viennent à l’esprit comme applications possibles, utilisées de manière responsable bien sûr.

12
ctcherry

La convention d'appellation class.__stuff indique au programmeur qu'il n'est pas censé accéder à __stuff de l'extérieur. Le nom mangling rend improbable que quelqu'un le fasse par accident.

Certes, vous pouvez toujours contourner ce problème, c'est encore plus facile que dans d'autres langages (BTW vous le permet également), mais aucun programmeur Python ne le ferait s'il se préoccupait d'encapsulation.

11
Nickolay

Avec Python 3.4, voici le comportement:

>>> class Foo:
        def __init__(self):
                pass
        def __privateMethod(self):
                return 3
        def invoke(self):
                return self.__privateMethod()


>>> help(Foo)
Help on class Foo in module __main__:

class Foo(builtins.object)
 |  Methods defined here:
 |
 |  __init__(self)
 |
 |  invoke(self)
 |
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |
 |  __dict__
 |      dictionary for instance variables (if defined)
 |
 |  __weakref__
 |      list of weak references to the object (if defined)

 >>> f = Foo()
 >>> f.invoke()
 3
 >>> f.__privateMethod()
 Traceback (most recent call last):
   File "<pyshell#47>", line 1, in <module>
     f.__privateMethod()
 AttributeError: 'Foo' object has no attribute '__privateMethod'

https://docs.python.org/3/tutorial/classes.html#tut-private

Notez que les règles de manipulation sont principalement conçues pour éviter les accidents; il est toujours possible d'accéder ou de modifier une variable considérée comme privée. Cela peut même être utile dans des circonstances spéciales, comme dans le débogueur.

Même si la question est ancienne, j'espère que mon extrait de code pourrait être utile.

3
Alberto

La préoccupation la plus importante concernant les méthodes et attributs privés est de dire aux développeurs de ne pas l'appeler en dehors de la classe. Il s'agit d'encapsulation. on peut mal comprendre la sécurité de l’encapsulation. quand on utilise délibérément la syntaxe comme celle que vous avez mentionnée (ci-dessous), vous ne voulez pas d'encapsulation.

obj._MyClass__myPrivateMethod()

J'ai migré de C # et au début, c’était bizarre pour moi aussi, mais après un moment, j’ai eu l’idée que seule la façon dont les concepteurs de code Python considèrent l’opération OOP est différente. 

2
Afshin Amiri

Pourquoi les méthodes "privées" de Python ne sont-elles pas réellement privées?

Si je comprends bien, ils ne peuvent pas être privés. Comment la vie privée pourrait-elle être appliquée?

La réponse évidente est "Les membres privés ne sont accessibles que par self", mais cela ne fonctionnerait pas - self n'est pas spécial en Python, ce n'est rien de plus qu'un nom couramment utilisé pour le premier paramètre d'une fonction.

0
user200783