Ocultando atributos y métodos (II)

La forma de "ocultar" (y verás en breve por qué lo pongo entre comillas) un atributo o un método es precediéndolo con dos guiones bajos:

class Circulo:
    
    __pi = 3.141592
    
    def __init__(self, radio):
        self.__radio = radio
    
    def __cuadrado(self, n):
        return n ** 2
    
    def area(self):
        return Circulo.__pi * self.__cuadrado(self.__radio)

(He creado un método que devuelve el cuadrado de un número solo para tener un método para ocultar y otro para no ocultar).

En el código anterior hemos ocultado el atributo de clase __pi, el atributo de objeto __radio y el método __cuadrado. Solo está accesible el método area. Probemos éste último y uno de los ocultos:

c = Circulo(2)

c.area()

12.566368

c.radio

Ocultando atributos y métodos

c.__radio

Ocultando atributos y métodos

Intentar acceder a estos atributos devuelve un error, pero ¿hemos conseguido ocultar realmente dichos atributos y métodos? Bueno, no del todo. Si volvemos a ejecutar la función dir sobre este objeto:

dir(c)

La función dir

...vemos que seguimos teniendo acceso a lo que parece un "alias" de los atributos y métodos ocultos:

c._Circulo__pi

3.141592

c._Circulo__radio

2

¡Upps! pues parece que seguimos teniendo acceso a los atributos ¿y podemos cambiarlos?

c._Circulo__radio = 4
c._Circulo__radio

4

A ver, calculemos el área nuevamente (antes era de 12.56):

c.area()

50.265472

Definitivamente tenemos acceso a los atributos. ¿Es esto un error de Python? Realmente no. Se trata de una decisión de diseño que, en muchos escenarios, es incluso preferible. En realidad hay formas de ocultar un método mediante programación, pero normalmente es innecesario llegar a ese punto.