Menu
Coddy logo textTech

Miembros públicos, protegidos y privados

Parte de la sección Object Oriented Programming del Journey de Python de Coddy — lección 28 de 64.

Python tiene tres niveles de control de acceso para los miembros de la clase: público, protegido y privado. Estos controlan cómo se puede acceder a los atributos y métodos.

Aquí hay un ejemplo de una clase con los tres niveles de acceso:

class BankAccount:
    def __init__(self, owner, balance, account_id):
        self.owner = owner           # Público - accesible en cualquier lugar
        self._balance = balance      # Protegido - uso interno
        self.__account_id = account_id  # Privado - solo clase
    
    def deposit(self, amount):       # Método público
        self._balance += amount
    
    def _calculate_interest(self):   # Método protegido
        return self._balance * 0.02
    
    def __validate_transaction(self, amount):  # Método privado
        return amount > 0 and amount <= self._balance

Accede a los miembros públicos desde cualquier lugar:

account = BankAccount("Alice", 1000, "12345")
print(account.owner)        # Alice
account.deposit(500)        # Funciona bien

Acceder a miembros protegidos (guion bajo simple - solo por convención):

print(account._balance)     # 1500 - funciona pero no se recomienda
result = account._calculate_interest()  # Funciona pero no se recomienda
print(result)               # 30.0

Intenta acceder a miembros privados (doble guion bajo - name mangling):

# Esto no funcionará:
# print(account.__account_id)  # AttributeError

# Pero esto funciona (name mangling):
print(account._BankAccount__account_id)  # 12345

Crea una subclase para mostrar el acceso protegido frente al privado:

class SavingsAccount(BankAccount):
    def show_balance(self):
        return self._balance        # Protegido - accesible en la subclase
    
    def show_id(self):
        # return self.__account_id  # Esto no funcionará - privado
        return "Cannot access private member"
savings = SavingsAccount("Bob", 2000, "67890")
print(savings.show_balance())  # 2000
print(savings.show_id())       # No se puede acceder al miembro privado

Salida:

Alice
30.0
12345
2000
Cannot access private member

Punto clave: Los miembros públicos no tienen prefijo y son accesibles desde cualquier lugar. Los miembros protegidos utilizan un guion bajo simple (_) y solo deben utilizarse dentro de la jerarquía de clases. Los miembros privados utilizan un guion bajo doble (__) y se les aplica el "name-mangling" (ofuscación de nombres) para una mayor privacidad. El control de acceso de Python se basa en convenciones, no se aplica de forma estricta.

challenge icon

Desafío

Intermedio

En este desafío, implementarás una clase BankAccount que demuestra el uso adecuado de los niveles de acceso público, protegido y privado en Python.

  • bankaccount.py - Contiene la definición de la clase con comentarios TODO que guían tu implementación
  1. Sigue los comentarios TODO en bankaccount.py para implementar la funcionalidad requerida
  2. Implementa los niveles de acceso adecuados (público, protegido, privado) según lo especificado
  3. Asegúrate de que todos los métodos manejen los casos de borde de manera apropiada (depósitos negativos, etc.)

Hoja de referencia

Python tiene tres niveles de control de acceso para los miembros de una clase:

  • Público: Sin prefijo, accesible desde cualquier lugar
  • Protegido: Guion bajo simple (_), para uso interno dentro de la jerarquía de clases
  • Privado: Guion bajo doble (__), con "name-mangling" para acceso exclusivo de la clase
class BankAccount:
    def __init__(self, owner, balance, account_id):
        self.owner = owner           # Público
        self._balance = balance      # Protegido
        self.__account_id = account_id  # Privado
    
    def deposit(self, amount):       # Método público
        self._balance += amount
    
    def _calculate_interest(self):   # Método protegido
        return self._balance * 0.02
    
    def __validate_transaction(self, amount):  # Método privado
        return amount > 0 and amount <= self._balance

Acceso a los miembros:

account = BankAccount("Alice", 1000, "12345")

# Acceso público
print(account.owner)        # Funciona correctamente

# Acceso protegido (funciona pero no se recomienda)
print(account._balance)     # 1500

# Acceso privado (con name mangling)
print(account._BankAccount__account_id)  # 12345

En subclases:

class SavingsAccount(BankAccount):
    def show_balance(self):
        return self._balance        # Protegido - accesible
    
    def show_id(self):
        # return self.__account_id  # Privado - no accesible
        return "Cannot access private member"

Pruébalo tú mismo

from bank_account import BankAccount

# Manejador integral de casos de prueba
test_case = input()

if test_case == "basic_test":
    # Prueba de funcionalidad básica
    account = BankAccount("John Doe", "12345")
    print(account.account_holder)  # Atributo público
    print(account.deposit(100))    # Método público
    print(account.get_balance())   # Método público

elif test_case == "transaction_count":
    # Probando atributo protegido
    account = BankAccount("Jane Smith", "67890")
    account.deposit(50)
    account.deposit(100)
    account.deposit(200)
    print(account._transaction_count)  # Atributo protegido
    print(account.get_balance())

elif test_case == "invalid_deposit":
    # Probando la validación en el método deposit
    account = BankAccount("Bob Johnson", "54321")
    print(account.deposit(-50))  # Debería devolver None
    print(account.deposit(0))    # Debería devolver None
    print(account.deposit(75))   # Debería devolver 75
    print(account.get_balance())

elif test_case == "private_access":
    # Probando el acceso a atributos privados
    account = BankAccount("Alice Brown", "98765")
    try:
        print(account.__balance)  # Esto lanzará un AttributeError
    except AttributeError:
        pass
    
    try:
        print(account.__account_number)  # Esto lanzará un AttributeError
    except AttributeError:
        pass
    
    print("Private attributes are not directly accessible")
    account.deposit(500)
    print(account.get_balance())  # Acceso a través de un método público

elif test_case == "name_mangling":
    # Demostrando el "name mangling" para atributos privados
    account = BankAccount("Charlie Green", "13579")
    print(account._BankAccount__balance)        # Accediendo a un atributo privado a través de "name mangling"
    print(account._BankAccount__account_number)  # Accediendo a un atributo privado a través de "name mangling"
    account.deposit(300)
    print(account._BankAccount__balance)  # Debería mostrar el saldo actualizado

elif test_case == "multiple_accounts":
    # Probando múltiples cuentas
    account1 = BankAccount("David White", "24680")
    account2 = BankAccount("Eva Black", "86420")
    account3 = BankAccount("Frank Red", "97531")
    
    account1.deposit(150)
    account2.deposit(250)
    account2.deposit(50)
    account3.deposit(500)
    account3.deposit(300)
    account3.deposit(200)
    
    print(f"{account1.account_holder}: {account1.get_balance()}")
    print(f"{account2.account_holder}: {account2.get_balance()}")
    print(f"{account3.account_holder}: {account3.get_balance()}")
    
    print(f"Account 1 transactions: {account1._transaction_count}")
    print(f"Account 2 transactions: {account2._transaction_count}")
    print(f"Account 3 transactions: {account3._transaction_count}")

elif test_case == "stress_test":
    # Prueba de rendimiento con muchas transacciones
    account = BankAccount("Stress Tester", "11111")
    for _ in range(1000):
        account.deposit(1)
    print(f"Final balance: {account.get_balance()}")
    print(f"Transaction count: {account._transaction_count}")
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Object Oriented Programming