¿Python tiene variables "privadas" en las clases?


578

Vengo del mundo Java y leo los patrones, recetas y modismos de Python 3 de Bruce Eckels .

Mientras lee sobre clases, continúa diciendo que en Python no hay necesidad de declarar variables de instancia. Solo los usas en el constructor, y boom, están allí.

Así por ejemplo:

class Simple:
    def __init__(self, s):
        print("inside the simple constructor")
        self.s = s

    def show(self):
        print(self.s)

    def showMsg(self, msg):
        print(msg + ':', self.show())

Si eso es cierto, cualquier objeto de la clase Simplepuede cambiar el valor de la variable sfuera de la clase.

Por ejemplo:

if __name__ == "__main__":
    x = Simple("constructor argument")
    x.s = "test15" # this changes the value
    x.show()
    x.showMsg("A message")

En Java, nos han enseñado sobre variables públicas / privadas / protegidas. Esas palabras clave tienen sentido porque a veces quieres variables en una clase a las que nadie fuera de la clase tenga acceso.

¿Por qué no se requiere eso en Python?


17
Te refieres a variables de instancia , no a variables de clase , ¿verdad?
PaulMcG el

13
Debe verificar las propiedades: docs.python.org/library/functions.html#property . Simplemente use el captador y su variable estará protegida.
rubik

Una respuesta corta y clara está aquí . Espero que esto sea de ayuda.
Premkumar chalmeti

Respuestas:


962

Es cultural. En Python, no escribes en la instancia de otras clases o variables de clase. En Java, nada le impide hacer lo mismo si realmente lo desea; después de todo, siempre puede editar el origen de la clase para lograr el mismo efecto. Python elimina esa pretensión de seguridad y alienta a los programadores a ser responsables. En la práctica, esto funciona muy bien.

Si desea emular variables privadas por algún motivo, siempre puede usar el __prefijo de PEP 8 . Python manipula los nombres de las variables __foopara que no sean fácilmente visibles para el código fuera de la clase que las contiene (aunque puede evitarlo si está lo suficientemente determinado, al igual que puede evitar las protecciones de Java si trabaja en él) )

Según la misma convención, el _prefijo significa mantenerse alejado incluso si técnicamente no se le impide hacerlo . No juegas con las variables de otra clase que parecen __fooo _bar.


14
Eso tiene sentido. Sin embargo, no creo que haya ninguna forma en Java de acceder a variables privadas fuera de la clase (excepto cambiar realmente la fuente de la clase del curso). ¿Esta ahí?
Omnipresente

168
Tiendo a preferir la forma de Python, pero no creo que la forma de Java sea tan inútil como se puede ver. Declarar algo privado rápidamente le dice a alguien que lee el código algo muy útil: este campo solo se modifica dentro de esta clase.
Ned

67
@Omnipresente, puedes usar la reflexión.
rapadura

77
Permítanme aclarar esto, por lo que Python no implementa atributos públicos ni privados porque "es una pretensión de seguridad y alienta a los programadores a ser responsables", sin embargo, la comunidad alienta el uso de "_" para denotar variables y métodos privados. ¿Quizás Python debería tener definitivamente no público y privado? Su objetivo principal es decirle qué API debe usar para interactuar con una clase. Sirven como documentación que le indica que use estos métodos y no los use. No son una "pretensión de seguridad", son documentación de API, ¡que incluso el IDE puede usar para guiarlo!
PedroD

19
Esta es una buena respuesta y su razonamiento es ciertamente válido, pero no estoy de acuerdo con un aspecto. El propósito de los modificadores de acceso nunca ha sido la seguridad . Más bien, son un medio de demarcar explícitamente (y en gran medida, hacer cumplir) qué partes de una clase se consideran internas y cuáles están expuestas a usuarios externos de esa clase. Las convenciones (cultura) son ciertamente una alternativa válida a los modificadores de acceso, y ambos métodos tienen sus pros y sus contras, pero es engañoso el propósito de que los modificadores de acceso a nivel de lenguaje pretenden de alguna manera ser "seguros" en el sentido habitual de palabra.
devios1

159

Las variables privadas en python son más o menos un truco: el intérprete renombra intencionalmente la variable.

class A:
    def __init__(self):
        self.__var = 123
    def printVar(self):
        print self.__var

Ahora, si intenta acceder __varfuera de la definición de clase, fallará:

 >>>x = A()
 >>>x.__var # this will return error: "A has no attribute __var"

 >>>x.printVar() # this gives back 123

Pero puedes salirte con la tuya fácilmente:

 >>>x.__dict__ # this will show everything that is contained in object x
               # which in this case is something like {'_A__var' : 123}

 >>>x._A__var = 456 # you now know the masked name of private variables
 >>>x.printVar() # this gives back 456

Probablemente sepa que los métodos en OOP se invocan así: x.printVar() => A.printVar(x)si se A.printVar()puede acceder a algún campo x, también se puede acceder a este campo fuera A.printVar() ... después de todo, las funciones se crean para la reutilización, no hay un poder especial para las declaraciones en el interior.

El juego es diferente cuando hay un compilador involucrado (la privacidad es un concepto de nivel de compilador ). Conoce la definición de clase con modificadores de control de acceso, por lo que puede generar un error si las reglas no se siguen en tiempo de compilación


3
en resumen, esto no es encapsulación
watashiSHUN

2
Me pregunto si PHP tiene algo similar con sus variables privadas tontas, ya que las variables privadas realmente no tienen sentido en lenguaje interpretado. Quiero decir, ¿qué optimización puede hacer sabiendo que x variable es privada, si no se compila?
NoBugs

1
¿Cómo podemos aleatorizar el patrón de variables privadas?
crisron

@crisron misma pregunta
IanS

55
@watashiSHUN "en resumen, esto no es encapsulación" => sí lo es. La encapsulación se trata solo de utilizar la API pública, por lo que el código del cliente está protegido de los cambios de implementación. Las convenciones de nomenclatura son una forma perfectamente válida de saber qué es API y qué es implementación, y el punto es que simplemente funciona.
bruno desthuilliers

30

Como se menciona correctamente en muchos de los comentarios anteriores, no olvidemos el objetivo principal de los modificadores de acceso: ayudar a los usuarios del código a comprender qué se supone que debe cambiar y qué no. Cuando ves un campo privado, no te equivocas con él. Por lo tanto, es principalmente azúcar sintáctico lo que se logra fácilmente en Python con _ y __.


44
Creo que este es un punto tan importante como cualquier otro. Al depurar código (lo sé, soy un debilucho para introducir errores), saber qué clases pueden cambiar una variable miembro simplifica el proceso de depuración. Al menos, si la variable está protegida por algún ámbito. Un concepto similar es const funciones en C ++. Yo que las variables miembro no ha cambiado allí y lo que ni siquiera se fijan en ese método como la causa potencial de una mala configuración de la variable. Aunque puede hacer el desarrollo posterior de extensiones de clase / agregar características, limitar la visibilidad del código facilita la depuración.
MrMas

18

Hay una variación de variables privadas en la convención de subrayado.

In [5]: class Test(object):
   ...:     def __private_method(self):
   ...:         return "Boo"
   ...:     def public_method(self):
   ...:         return self.__private_method()
   ...:     

In [6]: x = Test()

In [7]: x.public_method()
Out[7]: 'Boo'

In [8]: x.__private_method()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-8-fa17ce05d8bc> in <module>()
----> 1 x.__private_method()

AttributeError: 'Test' object has no attribute '__private_method'

Hay algunas diferencias sutiles, pero en aras de la pureza ideológica del patrón de programación, es lo suficientemente bueno.

Hay ejemplos de decoradores @private que implementan más estrechamente el concepto, pero YMMV. Podría decirse que también se podría escribir una definición de clase que use meta


14
Me doy cuenta de que es bastante tarde para la fiesta, pero este enlace aparece en Google cuando buscas en Google el problema. Esto no cuenta toda la historia. __xcomo Ael compilador reescribe una variable dentro de la clase _A__x, todavía no es completamente privada y aún se puede acceder a ella.
Zorf

1
Por supuesto, si veo una variable llamada _A__x, no la voy a tocar. Podría ser contagioso. Huiré muchísimo de eso.
Mateen Ulhaq

Justo seguro que no es un verdadero privado. Pero el razonamiento principal para la privacidad forzada en C ++ y Java (etc.), la optimización del compilador, realmente no existe en Python, por lo que la convención privada es lo suficientemente buena. La convención de Python generalmente es que confía en que te comportarás sin supervisión. (Y es una trampa para novatos, pero ya sabes, solo ten en cuenta el diseño y el consumo de la clase)
Shayne

14

"En Java, se nos ha enseñado sobre variables públicas / privadas / protegidas"

"¿Por qué no se requiere eso en Python?"

Por la misma razón, no es necesario en Java.

Eres libre de usar, o no usar privatey protected.

Como programador de Python y Java, he encontrado eso privatey protectedson conceptos de diseño muy, muy importantes. Sin embargo, como cuestión práctica, en decenas de miles de líneas de Java y Python, nunca he hecho acostumbrado privateo protected.

Por qué no?

Aquí está mi pregunta "¿protegido de quién?"

¿Otros programadores en mi equipo? Ellos tienen la fuente. ¿Qué significa protegido cuando pueden cambiarlo?

¿Otros programadores en otros equipos? Trabajan para la misma empresa. Pueden, con una llamada telefónica, obtener la fuente.

¿Clientela? Es una programación de trabajo por contrato (en general). Los clientes (generalmente) poseen el código.

Entonces, ¿de quién, precisamente, lo estoy protegiendo?


119
-1: Estoy de acuerdo con Porculus. No se trata de prohibir el acceso o esconder algo, se trata de documentación implícita de la API. Los desarrolladores, así como los compiladores / intérpretes / verificadores de código, ven fácilmente qué miembros se recomiendan usar y cuáles no deberían ser tocados (o al menos con cuidado). En la mayoría de los casos, sería un desastre horrible si todos los miembros de una clase o módulo fueran públicos. Considere la distinción de miembros privados / protegidos / públicos como un servicio, diciendo: "Hey, estos miembros son importantes mientras que se usan internamente y probablemente no sean útiles para usted".
Oben Sonne

77
@ S.Lott: Estoy de acuerdo en que los documentos API tienen mayor prioridad y, a menudo, son la única forma de comunicar los usos previstos de una API. Pero a veces los nombres de los miembros y la visibilidad (en términos de privado / público) hablan lo suficiente por sí mismos. Además, veo su punto de vista de que la idea de documentación implícita no funciona bien en editores sin inspección API, pero es realmente útil en IDE con finalización de código. Suponiendo que ya haya leído los documentos de la API hace algún tiempo, le ayuda a recordar cómo usar una clase. Las cosas no funcionarían de manera inteligente si no hubiera distinción entre miembros privados y públicos.
Oben Sonne

21
Tarde a la discusión, pero todo lo que solicitan Porculus y Oben aquí se maneja de manera perfectamente adecuada mediante la convención de "prefijo con un guión bajo" (y sin el daño que puede causar la aplicación del compilador de esa convención)
ncoghlan

38
@ S.Lott No soy un tipo de pitón, así que no voy a comentar desde esa perspectiva. Sin embargo, como desarrollador de Java, este es un consejo realmente horrible. -1
dbyrne

11
Guau. Olvida el punto por completo, da un consejo muy malo, insulta a cualquiera que no esté de acuerdo con usted en este punto, pero aún obtiene insignias y más de 1000 puntos de reputación por esta "respuesta".
Eric Duminil

12

Como se mencionó anteriormente, puede indicar que una variable o método es privado con el prefijo de subrayado. Si cree que esto no es suficiente, siempre puede usar el propertydecorador. Aquí hay un ejemplo:

class Foo:

    def __init__(self, bar):
        self._bar = bar

    @property
    def bar(self):
        """Getter for '_bar'."""
        return self._bar

De esta manera, alguien o algo que hace referencia en barrealidad está haciendo referencia al valor de retorno de la barfunción en lugar de la variable en sí, y por lo tanto se puede acceder pero no cambiarlo. Sin embargo, si alguien realmente quisiera, simplemente podría usarlo _bary asignarle un nuevo valor. No hay una forma segura de evitar que alguien acceda a las variables y métodos que desea ocultar, como se ha dicho repetidamente. Sin embargo, el uso propertyes el mensaje más claro que puede enviar que una variable no se debe editar.propertytambién se puede usar para rutas de acceso getter / setter / deleter más complejas, como se explica aquí: https://docs.python.org/3/library/functions.html#property


Django también aprecia esto.
babygame0ver

10

Python tiene soporte limitado para identificadores privados, a través de una función que antepone automáticamente el nombre de la clase a cualquier identificador que comience con dos guiones bajos. Esto es transparente para el programador, en su mayor parte, pero el efecto neto es que cualquier variable nombrada de esta manera puede usarse como variables privadas.

Ver aquí para más información sobre eso.

En general, la implementación de la orientación a objetos de Python es un poco primitiva en comparación con otros lenguajes. Pero disfruto esto, en realidad. Es una implementación muy simple desde el punto de vista conceptual y se adapta bien al estilo dinámico del lenguaje.


Sí. La belleza es que las capacidades de metaprogramación de Python significan que puedes implementar las cosas elegantes si lo deseas (y hay bibliotecas que implementan decoradores y cosas @ privadas / @ protegidas / etc. sin ninguna razón sensata), pero en la práctica no es tan necesario. Odio el meme "python / js / whatever is a lisp" porque casi nunca es cierto, pero python comparte 'chuletas de metaprogramación combinadas con sintaxis simple 'con ese idioma
Shayne

8

La única vez que uso variables privadas es cuando necesito hacer otras cosas al escribir o leer desde la variable y, como tal, necesito forzar el uso de un setter y / o getter.

Nuevamente, esto va a la cultura, como ya se dijo. He estado trabajando en proyectos donde leer y escribir variables de otras clases era gratuito para todos. Cuando una implementación quedó obsoleta, tomó mucho más tiempo identificar todas las rutas de código que usaban esa función. Cuando se forzó el uso de setters y getters, se pudo escribir fácilmente una declaración de depuración para identificar que se había llamado al método en desuso y la ruta del código que lo llama.

Cuando está en un proyecto en el que cualquiera puede escribir una extensión, notificar a los usuarios sobre los métodos obsoletos que desaparecerán en algunas versiones es vital para mantener la rotura del módulo al mínimo en las actualizaciones.

Entonces mi respuesta es; Si usted y sus colegas mantienen un conjunto de códigos simple, no siempre es necesario proteger las variables de clase. Si está escribiendo un sistema extensible, se vuelve imperativo cuando se realizan cambios en el núcleo que deben ser capturados por todas las extensiones que utilizan el código.


8

Lo siento muchachos por "resucitar" el hilo, pero espero que esto ayude a alguien:

En Python3 si solo quiere "encapsular" los atributos de la clase, como en Java, puede hacer lo mismo de esta manera:

class Simple:
    def __init__(self, str):
        print("inside the simple constructor")
        self.__s = str

    def show(self):
        print(self.__s)

    def showMsg(self, msg):
        print(msg + ':', self.show())

Para crear una instancia de esto, haga lo siguiente:

ss = Simple("lol")
ss.show()

Tenga en cuenta que: print(ss.__s)arrojará un error.

En la práctica, Python3 ofuscará el nombre del atributo global. Convirtiendo esto como un atributo "privado", como en Java. El nombre del atributo sigue siendo global, pero de forma inaccesible, como un atributo privado en otros idiomas.

Pero no tengas miedo de eso. No importa. También hace el trabajo. ;)


2
esto ha existido desde Python 1.5.2 IIRC, y aún no impide acceder al atributo a través de su nombre mutilado.
bruno desthuilliers

7

Los conceptos privados y protegidos son muy importantes. Pero Python: solo una herramienta para la creación de prototipos y el desarrollo rápido con recursos restringidos disponibles para el desarrollo, es por eso que algunos de los niveles de protección no son tan estrictos en Python. Puede usar "__" en el miembro de la clase, funciona correctamente, pero no parece lo suficientemente bueno: cada acceso a dicho campo contiene estos caracteres.

Además, puede notar que el concepto de Python OOP no es perfecto, pequeño o rubí mucho más cercano al concepto puro de OOP. Incluso C # o Java están más cerca.

Python es muy buena herramienta. Pero es un lenguaje OOP simplificado. Simplificada sintácticamente y conceptualmente. El objetivo principal de la existencia de Python es brindar a los desarrolladores la posibilidad de escribir código fácil de leer con un alto nivel de abstracción de una manera muy rápida.


La parte posterior de Private y Protected es importante es que en lenguajes compilados estáticamente el compilador puede crear llamadas directas al método privado, pero debe confiar en una tabla de búsqueda para métodos públicos. Esto simplemente no es un problema con lenguajes dinámicos. Finalmente, los lenguajes como C ++ tienen implicaciones para la herencia y la resolución de métodos. Python y Ruby tienen implementaciones muy similares de OO, por lo que la comparación no tiene sentido. Smalltalk en realidad no tiene noción de mensajes públicos / privados. Eres libre de agregar privado como categoría, pero es puramente asesor.
Shayne

Para promover mi afirmación. Desde el punto de vista de la codificación de la higiene, sí, son importantes para la encapsulación, pero no es necesario para ello, por lo que los decoradores @private (etc.) son más asesores que cualquier otra cosa, pero como privado / público no agrega nada útil para la optimización en un lenguaje no estático, no está implementado en un nivel profundo como lo haría en un lenguaje compilado como java o c
Shayne

4

Python no tiene variables privadas como C ++ o Java. También puede acceder a cualquier variable miembro en cualquier momento si lo desea. Sin embargo, no necesita variables privadas en Python, porque en Python no es malo exponer las variables miembro de sus clases. Si necesita encapsular una variable miembro, puede hacerlo utilizando "@property" más adelante sin romper el código de cliente existente.

En python, el guión bajo "_" se usa para indicar que un método o variable no se considera parte de la API pública de una clase y que esta parte de la API podría cambiar entre diferentes versiones. Puede usar estos métodos / variables, pero su código podría romperse si usa una versión más nueva de esta clase.

El doble guión bajo "__" no significa una "variable privada". Se usa para definir variables que son "clase local" y que no pueden ser fácilmente anuladas por subclases. Destroza el nombre de las variables.

Por ejemplo:

class A(object):
    def __init__(self):
        self.__foobar = None # will be automatically mangled to self._A__foobar

class B(A):
    def __init__(self):
        self.__foobar = 1 # will be automatically mangled to self._B__foobar

El nombre de self .__ foobar se desgarra automáticamente en self._A__foobar en la clase A. En la clase B se desgarra en self._B__foobar. Por lo tanto, cada subclase puede definir su propia variable __foobar sin anular sus variables principales. Pero nada le impide acceder a variables que comienzan con guiones bajos dobles. Sin embargo, el cambio de nombre le impide llamar a estas variables / métodos de manera incidental.

Recomiendo ver a Raymond Hettingers hablar sobre "Pythons class development toolkit" de Pycon 2013 (debería estar disponible en Youtube), que ofrece un buen ejemplo de por qué y cómo debe usar @property y "__" - variables de instancia.


Voy a revisar esa charla. ¿ @propertyForma parte de Python estándar o es específico de un IDE?
bballdave025

Es parte del estándar desde Python 2.6. Si debe usar una versión anterior, aún existe la posibilidad de usar la propertyfunción incorporada, que está disponible desde python 2.2
Hatatister del

0

En realidad, puedes simular un C#getter y setter usando este simple truco:

class Screen(object):

    def getter_setter_y(self, y, get=True):
        if get is True:
            Screen.getter_setter_y.value = y
        else:
            return Screen.getter_setter_y.value

     def getter_setter_x(self, x, get=True):
         if get is True:
             Screen.getter_setter_x.value = x
         else:
             return Screen.getter_setter_x.value

Luego úsalo de manera similar como en C#:

scr = Screen()
scr.getter_setter_x(100)
value =  scr.getter_setter_x(0, get=False)
print (value)

Simplemente declara una variable local estática en una función que desempeñará un rol get / set, ya que es la única forma de compartir una variable a través de métodos get y set, sin que sea global para una clase o archivo.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.