Solución:
Normalmente, el código de Python se esfuerza por adherirse al principio de acceso uniforme. Específicamente, el enfoque aceptado es:
- Exponga sus variables de instancia directamente, permitiendo, por ejemplo,
foo.x = 0
, nofoo.set_x(0)
- Si necesita envolver los accesos dentro de los métodos, por cualquier motivo, use
@property
, que conserva la semántica de acceso. Es decir,foo.x = 0
ahora invocafoo.set_x(0)
.
La principal ventaja de este enfoque es que la persona que llama puede hacer esto:
foo.x += 1
a pesar de que el código realmente podría estar haciendo:
foo.set_x(foo.get_x() + 1)
La primera declaración es infinitamente más legible. Sin embargo, con las propiedades, puede agregar (al principio o más adelante) el control de acceso que obtiene con el segundo enfoque.
Tenga en cuenta también que las variables de instancia que comienzan con un guión bajo son convencionalmente privado. Es decir, el subrayado indica a otros desarrolladores que consideras que el valor es privado y que no deberían meterse con él directamente; sin embargo, nada en el idioma previene ellos de jugar con él directamente.
Si usa un guión bajo doble (p. Ej., __x
), Python hace un poco de ofuscación del nombre. Sin embargo, la variable todavía es accesible desde fuera de la clase, a través de su nombre ofuscado. No es realmente privado. Es algo así como … más opaco. Y hay argumentos válidos en contra del uso del subrayado doble; por un lado, puede dificultar la depuración.
El “dunder” (doble subrayado, __
) el prefijo evita el acceso al atributo, excepto a través de accesos.
class Foo():
def __init__(self):
self.__attr = 0
@property
def attr(self):
return self.__attr
@attr.setter
def attr(self, value):
self.__attr = value
@attr.deleter
def attr(self):
del self.__attr
Algunos ejemplos:
>>> f = Foo()
>>> f.__attr # Not directly accessible.
Traceback (most recent call last):
File "<input>", line 1, in <module>
AttributeError: 'Foo' object has no attribute '__attr'
>>> '__attr' in f.__dir__() # Not listed by __dir__()
False
>>> f.__getattribute__('__attr') # Not listed by __getattribute__()
Traceback (most recent call last):
File "<input>", line 1, in <module>
AttributeError: 'Foo' object has no attribute '__attr'
>>> f.attr # Accessible by implemented getter.
0
>>> f.attr="Presto" # Can be set by implemented setter.
>>> f.attr
'Presto'
>>> f.__attr="Tricky?" # Can we set it explicitly?
>>> f.attr # No. By doing that we have created a
'Presto' # new but unrelated attribute, same name.
Sin embargo, puede acceder a este tipo de atributo a través de nombre destrozando (_classname__attribute
), que Python hace en segundo plano:
>>> f._Foo__attr
0
>>> f.__getattribute__('_Foo__attr')
0
Sencillamente, los principios de la programación orientada a objetos están equivocados. Por qué esto es una discusión larga que conduce a flamewars y probablemente no sea el tema de este sitio. 🙂
En Python no hay atributos privados, no puede protegerlos y esto nunca es un problema real. Así que no lo hagas. ¡Fácil! 🙂
Luego viene la pregunta: ¿Debería tener un guión bajo al principio o no? Y en el ejemplo que tienes aquí definitivamente no deberías. Un subrayado inicial en Python es una convención para mostrar que algo es interno, y no parte de la API, y que debe usarlo bajo su propio riesgo. Obviamente, este no es el caso aquí, pero es una convención común y útil.