¿Por qué los métodos “privados” de Python no son realmente privados?

Python nos da la capacidad de crear métodos y variables ‘privadas’ dentro de una clase añadiendo dos guiones bajos al nombre, como este: __myPrivateMethod() . ¿Cómo, entonces, se puede explicar esto?

 >>> 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!! 

¡¿Cual es el trato?!

Explicaré esto un poco para aquellos que no entendieron eso.

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

Lo que hice allí es crear una clase con un método público y un método privado y crear una instancia.

A continuación, llamo a su método público.

 >>> obj.myPublicMethod() public method 

A continuación, bash y llamo a su método privado.

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

Todo se ve bien aquí; no podemos llamarlo Es, de hecho, ‘privado’. Bueno, en realidad no lo es. Ejecutar dir () en el objeto revela un nuevo método mágico que Python crea mágicamente para todos tus métodos “privados”.

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

El nombre de este nuevo método siempre es un guión bajo, seguido del nombre de la clase, seguido del nombre del método.

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

Tanto para la encapsulación, ¿eh?

En cualquier caso, siempre escuché que Python no admite la encapsulación, ¿por qué incluso intentarlo? ¿Lo que da?

El cifrado de nombres se utiliza para garantizar que las subclases no anulen accidentalmente los métodos y atributos privados de sus superclases. No está diseñado para evitar el acceso deliberado desde el exterior.

Por ejemplo:

 >>> 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} 

Por supuesto, se descompone si dos clases diferentes tienen el mismo nombre.

Ejemplo de función privada.

 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 ### 

Cuando vine por primera vez de Java a Python, odiaba esto. Me asustó hasta la muerte.

Hoy podría ser lo que más amo de Python.

Me encanta estar en una plataforma, donde las personas confían entre sí y no sienten que necesitan construir muros impenetrables alrededor de su código. En lenguajes fuertemente encapsulados, si una API tiene un error, y ha descubierto qué es lo que no funciona, es posible que aún no pueda solucionarlo porque el método necesario es privado. En Python la actitud es: “seguro”. Si crees que entiendes la situación, quizás incluso la hayas leído, entonces todo lo que podemos decir es “¡buena suerte!”.

Recuerde, la encapsulación ni siquiera está débilmente relacionada con la “seguridad”, o mantener a los niños fuera del césped. Es solo otro patrón que debe usarse para hacer que una base de código sea más fácil de entender.

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

Estrictamente hablando, los métodos privados son accesibles fuera de su clase, pero no son fácilmente accesibles. Nada en Python es verdaderamente privado; Internamente, los nombres de los métodos y atributos privados son mutilados y desenredados sobre la marcha para que parezcan inaccesibles por sus nombres de stack. Puede acceder al método __parse de la clase MP3FileInfo con el nombre _MP3FileInfo__parse. Reconozca que esto es interesante, luego prometa nunca, nunca hacerlo en código real. Los métodos privados son privados por una razón, pero como muchas otras cosas en Python, su carácter privado es, en última instancia, una cuestión de convención, no de fuerza.

La frase comúnmente utilizada es “todos somos adultos que consienten aquí”. Al añadir un solo guión bajo (no exponer) o un doble guión bajo (ocultar), le está diciendo al usuario de su clase que pretende que el miembro sea “privado” de alguna manera. Sin embargo, confía en que todos los demás se comporten de manera responsable y respeten eso, a menos que tengan una razón convincente para no hacerlo (por ejemplo, los depuradores, la finalización del código).

Si realmente debe tener algo que es privado, entonces puede implementarlo en una extensión (por ejemplo, en C para CPython). En la mayoría de los casos, sin embargo, simplemente aprendes la forma pythonica de hacer las cosas.

No es como si no pudieras evitar la privacidad de los miembros en cualquier idioma (aritmética de punteros en C ++, Reflexiones en .NET / Java).

El punto es que recibe un error si intenta llamar al método privado por accidente. Pero si quieres dispararte en el pie, adelante, hazlo.

Edit: No intentas asegurar tus cosas por encapsulación OO, ¿verdad?

Existe un comportamiento similar cuando los nombres de los atributos de los módulos comienzan con un solo guión bajo (por ejemplo, _foo).

Los atributos de módulo nombrados como tales no se copiarán en un módulo de importación cuando se utilice el método from* , por ejemplo:

 from bar import * 

Sin embargo, esta es una convención y no una restricción de idioma. Estos no son atributos privados; Pueden ser referenciados y manipulados por cualquier importador. Algunos argumentan que debido a esto, Python no puede implementar una verdadera encapsulación.

Es solo una de esas opciones de diseño de idiomas. En algún nivel están justificados. Lo hacen así que necesitas ir muy lejos de tu camino para intentar llamar al método, y si realmente lo necesitas tanto, ¡debes tener una buena razón!

Los ganchos de depuración y las pruebas vienen a la mente como posibles aplicaciones, que se usan de manera responsable, por supuesto.

La convención de nomenclatura de class.__stuff permite al progtwigdor saber que no está destinado a acceder a class.__stuff desde el exterior. El nombre mangling hace que sea poco probable que alguien lo haga por accidente.

Es cierto que aún puedes solucionar esto, es incluso más fácil que en otros idiomas (lo que, por cierto, también te permite hacer esto), pero ningún progtwigdor de Python lo haría si le importa la encapsulación.

Con Python 3.4 este es el comportamiento:

 >>> 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 "", line 1, in  f.__privateMethod() AttributeError: 'Foo' object has no attribute '__privateMethod' 

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

Tenga en cuenta que las reglas de mutilación están diseñadas principalmente para evitar accidentes; Todavía es posible acceder o modificar una variable que se considera privada. Esto incluso puede ser útil en circunstancias especiales, como en el depurador.

Incluso si la pregunta es antigua, espero que mi fragmento pueda ser útil.

La preocupación más importante sobre los métodos y atributos privados es decirles a los desarrolladores que no lo llamen fuera de la clase y esto es encapsulación. uno puede malinterpretar la seguridad de la encapsulación. cuando uno usa deliberadamente una syntax como esa (abajo) que mencionó, no desea encapsulación.

 obj._MyClass__myPrivateMethod() 

He migrado de C # y al principio también fue extraño para mí, pero al cabo de un tiempo tuve la idea de que solo la forma en que los diseñadores de código de Python piensan acerca de la POO es diferente.

¿Por qué los métodos “privados” de Python no son realmente privados?

Como yo lo entiendo, no pueden ser privados. ¿Cómo se puede hacer cumplir la privacidad?

La respuesta obvia es que “solo se puede acceder a los miembros privados a través de self “, pero eso no funcionaría. self no es especial en Python, no es más que un nombre de uso común para el primer parámetro de una función.