¿Por qué los métodos "privados" de Python no son realmente privados?
Python nos brinda la capacidad de crear métodos y variables 'privados' dentro de una clase anteponiendo guiones bajos dobles 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 "<stdin>", line 1, in <module>
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 lo entendieron del todo.
>>> class MyClass:
... def myPublicMethod(self):
... print 'public method'
... def __myPrivateMethod(self):
... print 'this is private!!'
...
>>> obj = MyClass()
Creo una clase con un método público y un método privado y la instancia.
A continuación, llamo a su método público.
>>> obj.myPublicMethod()
public method
A continuación, intento llamar a su método privado.
>>> obj.__myPrivateMethod()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: MyClass instance has no attribute '__myPrivateMethod'
Todo se ve bien aquí; no podemos llamarlo. De hecho, es "privado". Bueno, en realidad no lo es. La ejecución dir()
del objeto revela un nuevo método mágico que Python crea mágicamente para todos sus 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!!
Hasta aquí la encapsulación, ¿eh?
En cualquier caso, siempre escuché que Python no admite la encapsulación, así que ¿por qué intentarlo? ¿Lo que da?
La codificación 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 impedir 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 estropea si dos clases diferentes tienen el mismo nombre.
Cuando vine por primera vez de Java a Python, odié esto . Me dio un susto de muerte.
Hoy en día, podría ser lo que más me gusta 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 usted ha descubierto qué es lo que falla, 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, tal vez incluso la hayas leído, entonces lo único que podemos decir es "¡buena suerte!".
Recuerde, la encapsulación no está ni siquiera débilmente relacionada con la "seguridad" o con mantener a los niños fuera del césped. Es solo otro patrón que debería usarse para hacer que una base de código sea más fácil de entender.
Ejemplo de una 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 beginning
matched = re.match( '^self\.', function_call)
if not matched:
print 'This is a private function. Go away.'
return
except:
print 'This is a private function. Go away.'
return
# This is the real function, only accessible inside the class #
print 'Hey, welcome in to the function.'
def public_function(self):
# I can call a private function from inside the class
self.private_function()
### End ###
De Dive Into Python, 3.9. Funciones privadas :
Estrictamente hablando, se puede acceder a los métodos privados fuera de su clase, pero no es fácilmente accesible. Nada en Python es verdaderamente privado; Internamente, los nombres de los métodos y atributos privados se mutilan y desmenuzan sobre la marcha para que parezcan inaccesibles por sus nombres de pila. Puede acceder al método __parse de la clase MP3FileInfo con el nombre _MP3FileInfo__parse. Reconozca que esto es interesante y luego prometa no hacerlo nunca en código real. Los métodos privados son privados por una razón, pero como muchas otras cosas en Python, su privacidad es, en última instancia, una cuestión de convención, no de fuerza.
La frase comúnmente utilizada es "aquí todos somos adultos que consienten". Al anteponer un guión bajo simple (no exponer) o un guión bajo doble (ocultar), le estás diciendo al usuario de tu clase que deseas que el miembro sea "privado" de alguna manera. Sin embargo, usted confía en que todos los demás se comportarán de manera responsable y lo respetarán, a menos que tengan una razón de peso para no hacerlo (por ejemplo, depuradores y finalización de código).
Si realmente necesita tener algo que sea privado, puede implementarlo en una extensión (por ejemplo, en C para CPython ). Sin embargo, en la mayoría de los casos, simplemente aprendes la forma Pythonic de hacer las cosas.