CodeGym /Cursos /Python SELF ES /Métodos y campos especiales

Métodos y campos especiales

Python SELF ES
Nivel 15 , Lección 5
Disponible

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 :)

Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION