- ¿Qué es la herencia?
- ¿Cómo se controla el acceso a los atributos y métodos?
- Tipos de acceso en Python
- Atributos y métodos públicos
- Atributos y métodos privados
- Atributos y métodos protegidos
- Name Mangling: cómo funcionan los nombres privados
- ¿Cuál estilo usar?
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 |
Sí | Sí |
| 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
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.