method - ¿Python tiene variables "privadas" en las clases?
python access modifiers (11)
"En java, nos han enseñado sobre variables públicas / privadas / protegidas"
"¿Por qué no se requiere eso en python?"
Por la misma razón no se requiere en Java.
Usted es libre de usar, o no usar private
y protected
.
Como programador de Python y Java, descubrí que lo private
y lo protected
son conceptos de diseño muy, muy importantes. Pero como cuestión práctica, en decenas de miles de líneas de Java y Python, nunca he usado private
o 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 la programación de trabajo por contrato (en general). Los clientes (generalmente) poseen el código.
Entonces, ¿de quién, precisamente, estoy protegiéndolo?
Derecha. El sociópata esquizofrénico que se negó a leer los bloques de comentarios API.
Vengo del mundo Java y leo los patrones, recetas y modismos Python 3 de Bruce Eckels.
Mientras lee sobre las 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 ahí.
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, entonces cualquier objeto de la clase Simple
puede cambiar el valor de la variable s
fuera 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, se nos ha enseñado sobre variables públicas / privadas / protegidas. Esas palabras clave tienen sentido porque a veces se desean variables en una clase a la que nadie fuera de la clase tiene acceso.
¿Por qué no se requiere en Python?
Como lo mencionamos correctamente en muchos de los comentarios anteriores, no olvidemos el objetivo principal de los modificadores de acceso: ayudar a los usuarios de código a comprender qué se supone que debe cambiar y qué se supone que no debe hacerlo. Cuando ves un campo privado no te metes con eso. Así que es principalmente azúcar sintáctica que se logra fácilmente en Python por _ y __.
Como se mencionó anteriormente, puede indicar que una variable o método es privado prefijándolo con un guión bajo. Si no cree que esto sea suficiente, siempre puede usar el decorador de property
. 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 a la bar
realidad hace referencia al valor de retorno de la función de la bar
lugar de a la variable en sí, y por lo tanto se puede acceder pero no cambiar. Sin embargo, si alguien realmente quisiera, simplemente podría usar _bar
y asignarle un nuevo valor. No hay una manera segura de evitar que alguien acceda a las variables y los métodos que desea ocultar, como se ha dicho repetidamente. Sin embargo, usar la property
es el mensaje más claro que puede enviar para que no se edite una variable. property
también se puede utilizar para rutas de acceso más complejas de obtención / establecimiento / eliminación, como se explica aquí: https://docs.python.org/3/library/functions.html#property
Es cultural En Python, no escribes en las variables de instancia o clase de otras clases. En Java, nada le impide hacer lo mismo si realmente quiere hacerlo; después de todo, siempre puede editar la fuente de la clase para lograr el mismo efecto. Python deja esa simulació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 alguna razón, siempre puede usar el prefijo __
de PEP 8 . Python maneja los nombres de variables como __foo
para que no sean fácilmente visibles para el código fuera de la clase que las contiene (aunque puede __foo
si está suficientemente determinado, al igual que puede evitar las protecciones de Java si trabaja en eso).
Por la misma convención, el prefijo _
significa mantenerse alejado, incluso si no está técnicamente impedido de hacerlo . No juegas con las variables de otra clase que se parecen a __foo
o _bar
.
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''
Existen algunas diferencias sutiles, pero para la programación de la pureza ideológica, es lo suficientemente bueno.
Hay ejemplos por ahí de decoradores @private que implementan más de cerca el concepto, pero YMMV. Podría decirse que también se podría escribir una definición de clase que utiliza meta
La única vez que uso variables privadas es cuando necesito hacer otras cosas cuando escribo o leo la variable y, como tal, necesito forzar el uso de un setter y / o getter.
De nuevo esto va a la cultura, como ya se ha dicho He estado trabajando en proyectos donde leer y escribir otras variables de clases era gratis para todos. Cuando una implementación quedó en desuso, 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 podría 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 se encuentra en un proyecto en el que cualquiera puede escribir una extensión, notificar a los usuarios sobre métodos obsoletos que desaparecerán en algunas versiones, por lo tanto, es vital para mantener la ruptura del módulo al mínimo después de las actualizaciones.
Así que mi respuesta es; Si usted y sus colegas mantienen un conjunto de códigos simple, proteger las variables de clase no siempre es necesario. Si está escribiendo un sistema extensible, entonces es imperativo cuando se realizan cambios en el núcleo que deben ser capturados por todas las extensiones usando el código.
Las variables privadas en Python son más o menos un hack: 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 a __var
fuera 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 fácilmente puede salirse con la suya con esto:
>>>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 A.printVar()
puede acceder a algún campo en x
, también se puede acceder a este campo fuera de A.printVar()
... después de todo, las funciones se crean para su reutilización, no hay un poder especial otorgado a las declaraciones internas.
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 producir un error si no se siguen las reglas en el momento de la compilación
Lo siento, chicos por "resucitar" el hilo, pero espero que esto ayude a alguien:
En Python3, si solo quieres "encapsular" los atributos de clase, como en Java, puedes 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 instanciar esto haz:
ss = Simple("lol")
ss.show()
Tenga en cuenta que: print(ss.__s)
producirá un error.
En la práctica, Python3 ofuscará el nombre del atributo global. Volviendo 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 le tengas miedo. No importa. Hace el trabajo también. ;)
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 limitados disponibles para el desarrollo, es por eso que algunos 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 OOP de python no es perfecto, smaltalk o ruby mucho más cercano al concepto OOP puro. Incluso C # o Java están más cerca.
Python es una muy buena herramienta. Pero es un lenguaje OOP simplificado. Sintácticamente y conceptualmente simplificado. El objetivo principal de la existencia de Python es brindar a los desarrolladores la posibilidad de escribir código de fácil lectura con un alto nivel de abstracción de una manera muy rápida.
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 tiene la necesidad de encapsular una variable miembro, puede hacerlo usando "@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 del api público de una clase y que esta parte del 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 subrayado doble "__" no significa una "variable privada". Se usa para definir variables que son "de clase local" y que no pueden ser fácilmente ocultadas por las subclases. Maneja 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
self .__ El nombre de foobar se transforma automáticamente en self._A__foobar en la clase A. En la clase B, se maneja a self._B__foobar. Por lo tanto, cada subclase puede definir su propia variable __foobar sin anular sus variables primarias. Pero nada le impide acceder a las variables que comienzan con guiones bajos. Sin embargo, la manipulación de nombres le impide llamar a estas variables / métodos de forma incidental.
Recomiendo ver a Raymond Hettingers hablar "Pythons class development toolkit" de Pycon 2013 (debería estar disponible en Youtube), lo que da un buen ejemplo de por qué y cómo debe usar @property y "__" - variables de instancia.
Python tiene soporte limitado para identificadores privados, a través de una característica que automáticamente antepone 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.
Vea here para más sobre eso.
En general, la implementación de Python de la orientación a objetos es un poco primitiva en comparación con otros lenguajes. Pero me gusta esto, en realidad. Es una implementación muy simple desde el punto de vista conceptual y encaja bien con el estilo dinámico del lenguaje.