Regístrate para acceder a más de 15 cursos gratuitos de programación con un simulador

Modificadores de acceso Python: Profundizando en las clases

En esta lección vamos a hablar sobre un tema clave en la programación orientada a objetos: la herencia y cómo controlamos el acceso a las partes internas de nuestros objetos. Veremos cómo Python nos permite (aunque no obliga) ocultar atributos y métodos para generar clases más organizadas y resistentes a errores.


¿Qué es la herencia?

La herencia es un mecanismo que nos permite crear nuevas clases a partir de otras ya existentes.

En otras palabras, una clase hija (subclase) puede “heredar” atributos y métodos de una clase padre (superclase), lo que evita duplicar código y fomenta la reutilización.

Por ejemplo, supongamos que tenemos una clase básica llamada ElementoHTML que representa cualquier elemento HTML. Luego podemos crear una clase hija ElementoDiv que herede sus propiedades y métodos.


¿Cómo se controla el acceso a los atributos y métodos?

En Python no existe una forma estricta de ocultar partes del código como en otros lenguajes (como Java o C++). Sin embargo, usamos ciertas convenciones para indicar hasta qué punto se debería acceder o modificar algo.

Estas convenciones nos ayudan a:

  • Proteger los datos internos del objeto
  • Indicar qué partes están pensadas para ser utilizadas desde fuera
  • Prevenir errores al extender clases con herencia

Tipos de acceso en Python

Tipo de acceso Cómo se define ¿Es accesible desde clases hijas? ¿Es accesible desde fuera?
Público nombre
Protegido _nombre Sí (pero debería usarse con cuidado) Sí (aunque no recomendado)
Privado __nombre No directamente No directamente

Atributos y métodos públicos

Son aquellos que se definen normalmente, sin guiones bajos.

Son accesibles desde cualquier parte: dentro de la clase, desde las clases hijas, y desde fuera del objeto.

Veamos un ejemplo práctico:

class ElementoHTML:
    def __init__(self):
        self.visible = True

    def es_visible(self):
        return self.visible

class ElementoDiv(ElementoHTML):
    def acceder_propiedad_heredada(self):
        return self.visible

    def acceder_metodo_heredado(self):
        return self.es_visible()

div = ElementoDiv()

# Acceso directo desde fuera
print(div.visible)                 # True
print(div.es_visible())           # True

# Acceso desde métodos de la subclase
print(div.acceder_propiedad_heredada())  # True
print(div.acceder_metodo_heredado())     # True

Las clases hijas también pueden heredar estos atributos automáticamente, incluso si hay más niveles intermedios:

class OtroElementoDiv(ElementoDiv):
    pass

nuevo_div = OtroElementoDiv()
print(nuevo_div.visible)          # True
print(nuevo_div.es_visible())     # True

Y algo muy importante: aunque estas variables se hereden, cada objeto tiene su propia copia:

div1 = ElementoDiv()
div2 = ElementoDiv()

div1.visible = False
print(div1.visible)  # False
print(div2.visible)  # True

Esto es porque visible es un atributo de instancia.


Atributos y métodos privados

Ahora veamos los atributos y métodos privados. Estos no deberían ser accedidos fuera de la clase donde se definieron.

En Python, para hacer algo privado, simplemente usamos doble guión bajo (__) al inicio del nombre.

class ElementoHTML:
    def __init__(self):
        self.__visible = True

    def es_visible(self):
        return self.__visible

class ElementoDiv(ElementoHTML):
    pass

div = ElementoDiv()

print(div.es_visible())       # True
print(div.__visible)          # AttributeError

Como ves, no se puede acceder a __visible directamente desde fuera ni desde la subclase. Esto nos ayuda a ocultar información importante que no queremos que otros modifiquen.


Atributos y métodos protegidos

Los atributos protegidos se escriben con un solo guión bajo (_). Esto es más una sugerencia que una prohibición: se pueden usar desde fuera o en subclases, pero lo recomendable es no hacerlo directamente.

class ElementoHTML:
    def __init__(self):
        self._visible = True  # Atributo protegido

    def es_visible(self):
        return self._visible

class ElementoDiv(ElementoHTML):
    def acceder_visible(self):
        return self._visible

div = ElementoDiv()
print(div.es_visible())          # True
print(div.acceder_visible())     # True
print(div._visible)              # True (pero mala práctica)

¿Ves la diferencia? Con un subrayado simple sí podemos acceder desde fuera, pero no debemos hacerlo a menos que sepamos muy bien lo que estamos haciendo.


Name Mangling: cómo funcionan los nombres privados

Cuando usamos doble subrayado (__) en un atributo privado, Python aplica algo llamado name mangling: internamente cambia el nombre de ese atributo para evitar que las clases hijas lo reemplacen sin querer.

Miremos este ejemplo:

class Usuario:
    def __init__(self):
        self.nombre = "Usuario"

    def __saludo_privado(self):
        return f"Hola, {self.nombre}!"

    def saludar(self):
        return self.__saludo_privado()

class Juan(Usuario):
    def __saludo_privado(self):
        return "Hola, soy Juan!"

print(Juan().saludar())  # Hola, Usuario!

Aunque Juan definió un nuevo __saludo_privado, no está sobreescribiendo el del padre. Cada uno termina con un nombre distinto internamente.

Podemos confirmar esto utilizando la función dir():

dir(Juan)
# ['_Juan__saludo_privado', '_Usuario__saludo_privado', ...]

Pero ojo: este "truco" no impide a alguien que sepa lo que está haciendo acceder igualmente:

class Juan(Usuario):
    def _Usuario__saludo_privado(self):
        return "Hola, soy Juan!"

print(Juan().saludar())  # Hola, soy Juan!

Sí, se puede "hackear" el nombre original, pero hacerlo está fuertemente desaconsejado.


¿Cuál estilo usar?

Cuando diseñamos nuestras clases, debemos pensar qué cosas queremos que otros usen directamente y cuáles deberían quedar ocultas. Una buena práctica es:

  • Usar un nombre normal para métodos y atributos públicos.
  • Empezar con un solo guión bajo (_) para indicar que algo es interno.
  • Usar doble guión bajo (__) solo cuando necesitamos aislamiento total entre clases que pueden heredar unas de otras.

Mientras podamos resolver un problema usando métodos públicos y protegidos, evitemos complicarnos con los privados.


Resumen

  • La herencia nos permite compartir propiedades y comportamiento entre clases.
  • Los atributos y métodos públicos son accesibles desde cualquier parte.
  • Los protegidos (_nombre) deben usarse solo dentro de la clase y sus hijas.
  • Los privados (__nombre) no son accesibles ni por clases hijas ni desde fuera directamente.
  • Python usa name mangling para evitar que los privados sean sobrescritos sin querer.
  • No existen restricciones reales en Python, todo es por convención. ¡Respetémoslas para escribir buen código!

Materiales adicionales

  1. Acuerdo entre desarrolladores.

Para acceder completo a curso necesitas un plan básico

El plan básico te dará acceso completo a todos los cursos, ejercicios y lecciones de Códica, proyectos y acceso de por vida a la teoría de las lecciones completadas. La suscripción se puede cancelar en cualquier momento.

Obtener acceso
130
cursos
1000
ejercicios
2000+
horas de teoría
3200
test

Obtén acceso

Cursos de programación para principiantes y desarrolladores experimentados. Comienza tu aprendizaje de forma gratuita

  • 130 cursos, 2000+ horas de teoría
  • 1000 ejercicios prácticos en el navegador
  • 360 000 estudiantes
Al enviar el formulario, aceptas el «Política de privacidad» y los términos de la «Oferta», y también aceptas los «Términos y condiciones de uso»

Nuestros graduados trabajan en empresas como:

Bookmate
Health Samurai
Dualboot
ABBYY