it-swarm-es.com

¿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 al añadir dobles guiones bajos al nombre, como esto: __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, intento 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?

596
willurd

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.

548
Alya

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

La primera vez que vine de Java a Python I odié esto. Me asustó hasta la muerte.

Hoy podría ser lo único Me encanta más sobre 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.

146
Thomas Ahle

Desde 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 pila. 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.

139
xsl

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 sea 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 pitónica de hacer las cosas.

87
Tony Meyer

No es como si no pudieras evitar la privacidad de los miembros en cualquier idioma (punteros aritméticos 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?

32
Maximilian

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

12
Ross

La convención de nomenclatura class.__stuff le permite al programador saber que no está destinado a acceder a __stuff desde fuera. 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 programador de Python lo haría si le importa la encapsulación.

12
Nickolay

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.

12
ctcherry

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

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 puede ser útil incluso en circunstancias especiales, como en el depurador.

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

4
Alberto

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 sintaxis como esa (abajo) que mencionó, no desea encapsulación.

obj._MyClass__myPrivateMethod()

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

2
Afshin Amiri

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

Como yo lo entiendo, ellos 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 comúnmente usado para el primer parámetro de una función.

0
user200783