Menu
CoddyTech

Miembros públicos, protegidos y privados

Parte de la sección Programación orientada a objetos del Journey de Python de Coddy. Lección 28 de 64.

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

Aquí tienes 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 desde cualquier lugar
        self._balance = balance      # Protegido - uso interno
        self.__account_id = account_id  # Privado - solo para la 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

Accede a los 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 los miembros privados (doble guion bajo: nombre alterado):

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

# Pero esto sí funciona (alteración de nombres):
print(account._BankAccount__account_id)  # 12345

Crea una subclase para mostrar la diferencia entre el acceso protegido y el privado:

class SavingsAccount(BankAccount):
    def show_balance(self):
        return self._balance        # Protegido - accesible desde una subclase
    
    def show_id(self):
        # return self.__account_id  # Esto no funcionará - es 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

Resultado:

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 usan un guion bajo (_) y solo deben utilizarse dentro de la jerarquía de clases. Los miembros privados usan dos guiones bajos (__) y sus nombres se modifican para ofrecer mayor privacidad. El control de acceso de Python se basa en convenciones y no se aplica de manera 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 orientan 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 y privado) según se especifica
  3. Asegúrate de que todos los métodos gestionen adecuadamente los casos límite (depósitos negativos, etc.)

Pruébalo tú mismo

from bank_account import BankAccount

# Gestor 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":
    # Prueba del 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":
    # Prueba de 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":
    # Prueba de acceso a atributos privados
    account = BankAccount("Alice Brown", "98765")
    try:
        print(account.__balance)  # Esto provocará un AttributeError
    except AttributeError:
        pass
    
    try:
        print(account.__account_number)  # Esto provocará un AttributeError
    except AttributeError:
        pass
    
    print("Private attributes are not directly accessible")
    account.deposit(500)
    print(account.get_balance())  # Acceso mediante un método público

elif test_case == "name_mangling":
    # Demostración de la modificación de nombres para atributos privados
    account = BankAccount("Charlie Green", "13579")
    print(account._BankAccount__balance)        # Acceso a un atributo privado mediante la modificación de nombres
    print(account._BankAccount__account_number)  # Acceso a un atributo privado mediante la modificación de nombres
    account.deposit(300)
    print(account._BankAccount__balance)  # Debería mostrar el saldo actualizado

elif test_case == "multiple_accounts":
    # Prueba de varias 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 Programación orientada a objetos

Practica por tu cuenta: Compilador de Python online