5.1 Niveles de acceso
Probablemente has notado el nombre extraño del constructor __init__
? En el futuro te encontrarás con esto bastante a menudo.
En Python existen diferentes niveles de acceso a atributos y métodos de clases, que ayudan a controlar la visibilidad y protección de los datos. Los principales mecanismos de control de acceso incluyen el uso de uno o dos guiones bajos (_
y __
) antes del nombre del atributo o método.
Uso de convenciones
Un guion bajo _
se utiliza para atributos y métodos que no deberían usarse fuera de la clase o módulo. No está prohibido, pero es una convención que debe seguirse para una mejor legibilidad y mantenimiento del código.
Dos guiones bajos __
se utilizan para atributos y métodos que deben ser realmente privados y protegidos de acceso accidental o intencionado desde el exterior. El mecanismo name mangling
los hace menos accesibles, pero aún disponibles a través de nombres especiales.
Campos y métodos (public)
públicos
Atributos y métodos públicos están disponibles desde cualquier lugar del código
. En Python, por defecto todos los atributos y métodos son públicos, si sus nombres no comienzan con un guion bajo.
class MyClass:
def __init__(self):
self.public_attribute = "I am public"
def public_method(self):
return "This is a public method"
obj = MyClass()
print(obj.public_attribute) # Disponible
print(obj.public_method()) # Disponible
A través de los niveles de acceso en el lenguaje Python se implementa la encapsulación, es decir, a través de campos y métodos no públicos.
5.2 Campos y métodos no públicos
Campos y métodos (protected)
protegidos
Atributos y métodos protegidos se denotan con un guion bajo _
antes del nombre y están destinados para uso interno dentro de la clase y sus subclases. Es una convención que indica a los programadores que los datos no están destinados para uso fuera de la clase.
class MyClass:
def __init__(self):
self._protected_attribute = "I am protected"
def _protected_method(self):
return "This is a protected method"
obj = MyClass()
print(obj._protected_attribute) # Disponible, pero no recomendado
print(obj._protected_method()) # Disponible, pero no recomendado
Campos y métodos (private)
privados
En Python los atributos y métodos privados se denotan con dos guiones bajos __
antes del nombre. Estos atributos y métodos están destinados al uso exclusivo dentro de la clase, y su propósito principal es ocultar la implementación interna y proteger los datos de cambios accidentales o uso desde el exterior.
Para prevenir el acceso directo a tales atributos y métodos desde código externo, Python aplica un mecanismo especial, conocido como name mangling
(distorsión de nombre). Este mecanismo cambia automáticamente los nombres de los atributos privados, añadiéndoles un prefijo que consiste en el nombre de la clase. Así, el atributo privado __private_attribute
en la clase MyClass
se transformará en el nombre interno _MyClass__private_attribute
.
Esto permite proteger los datos de acceso no intencionado, manteniendo al mismo tiempo la posibilidad de trabajar con ellos dentro de la clase. Sin embargo, es importante recordar que el mecanismo de "name mangling"
no es una protección absoluta — un programador experimentado puede acceder a estos datos utilizando el nombre modificado.
Vamos a ver cómo funciona esto en la práctica:
class MyClass:
def __init__(self):
self.__private_attribute = "I am private"
def __private_method(self):
return "This is a private method"
def access_private_method(self):
return self.__private_method()
obj = MyClass()
# print(obj.__private_attribute) # Error, no disponible directamente
# print(obj.__private_method()) # Error, no disponible directamente
print(obj.access_private_method()) # Disponible a través de un método público de la clase
Como se puede ver en el ejemplo, el acceso directo a atributos o métodos privados genera un error. Pero Python mantiene la posibilidad de acceder a ellos a través del nombre modificado. Por ejemplo, puedes acceder a un atributo privado utilizando el nombre "distorsionado", como se muestra a continuación:
class MyClass:
def __init__(self):
self.__private_attribute = "I am private"
obj = MyClass()
print(obj._MyClass__private_attribute) # Muestra: I am private
Aunque el acceso a través del nombre "distorsionado" es posible, se debe evitar, ya que esto infringe los principios de encapsulación y puede llevar a la inestabilidad del código.
Para ver cómo Python cambia los nombres de los atributos, puedes usar la función incorporada dir()
, que muestra todos los atributos y métodos disponibles de un objeto:
class MyClass:
def __init__(self):
self.__private_attribute = "I am private"
obj = MyClass()
print(dir(obj)) # Muestra todos los atributos y métodos del objeto, incluyendo los nombres "distorsionados"
Como resultado de ejecutar la función dir()
verás una lista de todos los atributos y métodos del objeto, incluyendo _MyClass__private_attribute
, lo que confirma el mecanismo de "name mangling"
.
5.3 Llamada automática de métodos
Hubo un aspecto interesante al trabajar con constructores, al que quizás hayas prestado atención. El método __init__
se llamó automáticamente.
De hecho, hay muchas situaciones así, como también métodos para estos casos. Ejemplos:
Método __str__
Si tu objeto tiene un método __str__
, entonces será llamado automáticamente al intentar convertir tu objeto en una cadena, por ejemplo, al usar las funciones print()
y str()
.
class Cat:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"{self.name} is {self.age} years old"
cat = Cat("Barsik", 5)
print(cat) # Muestra: Barsik is 5 years old
Método __len__
Y si tu objeto tiene un método __len__
, entonces será llamado automáticamente al intentar determinar la "longitud" de tu objeto — utilizado por la función len()
. Ejemplo:
class MyList:
def __init__(self, items):
self.items = items
def __len__(self):
return len(self.items)
my_list = MyList([1, 2, 3])
print(len(my_list)) # Muestra: 3
Habrá muchos más de estos "métodos especiales" en tu vida, pero trabajar con ellos es un placer. Así que prepárate :)
GO TO FULL VERSION