Menu
CoddyTech

Atributos privados

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

Los atributos privados usan guiones bajos para indicar que no se debe acceder directamente a ciertos datos desde fuera de la clase.

¿Por qué necesitamos atributos privados? Imagina que tienes una cuenta bancaria. No quieres que nadie cambie directamente tu saldo: debería hacerlo a través de los canales adecuados (métodos de depósito/retiro) para que el banco pueda validar la transacción, comprobar si hay fraude, llevar registros, etc. Los atributos privados funcionan de la misma manera: protegen tus datos para evitar que se modifiquen incorrectamente.

Aquí tienes un ejemplo que utiliza un guion bajo (convención para «uso interno»):

class Person:
    def __init__(self, name, age):
        self._name = name    # "protected" - uso interno
        self._age = age      # "protected" - uso interno

Se puede seguir accediendo a los atributos con un solo guion bajo, pero es una señal de que no deberías hacerlo:

person = Person("Alice", 30)
print(person._name)  # Funciona, pero no se recomienda

Usa guiones bajos dobles para una mayor privacidad (alteración de nombres):

class Person:
    def __init__(self, name, age):
        self.__name = name   # "private" - se le aplica la modificación de nombres
        self.__age = age     # "private" - se le aplica la modificación de nombres
    
    def get_name(self):
        return self.__name
    
    def set_age(self, age):
        if age >= 0:
            self.__age = age
        else:
            print("Age must be positive!")

¿Por qué usar métodos getter y setter? Son como guardianes. En lugar de permitir que cualquiera cambie __age directamente (lo que podría dar lugar a edades negativas u otros datos no válidos), obligamos a usar set_age(), que primero valida la entrada. Esto evita errores y garantiza la integridad de los datos.

El «código extra» tiene un propósito:

  • get_name() - Acceso controlado para leer el nombre
  • set_age(age) - Valida la edad antes de permitir cambios (¡no se permiten edades negativas!)
  • Sin estos métodos, alguien podría establecer age = -100 y estropear tu programa

Usa los métodos de acceso para interactuar con los atributos privados:

person = Person("Bob", 25)
print(person.get_name())  # Bob

person.set_age(30)        # Válido: la edad pasa a ser 30
person.set_age(-5)        # No válido: ¡la edad debe ser positiva! (la edad permanece en 30)

¿Ves la ventaja? El método set_age() evita los datos no válidos. Sin él, podrías crear accidentalmente una persona con edad -5, ¡lo cual no tiene sentido!

A los atributos con doble guion bajo se les modifica el nombre, pero aún se puede acceder a ellos:

person = Person("Charlie", 35)
# Esto no funciona:
# print(person.__name)  # AttributeError

# Pero esto funciona (no recomendado):
print(person._Person__name)  # Charlie

Ejemplo del mundo real: ¿por qué todo este código?

class BankAccount:
    def __init__(self, balance):
        self.__balance = balance  # Privado: no se puede modificar directamente
    
    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
            return True
        return False
    
    def get_balance(self):
        return self.__balance

# Sin atributos privados:
# account.__balance = -1000000  # ¡Desastre! Se permite un saldo negativo

# Con atributos privados:
account = BankAccount(100)
account.deposit(50)           # Seguro: validado
print(account.get_balance())  # 150

El «código adicional» (los métodos) protege tus datos de cambios no válidos. Es como tener guardias de seguridad en lugar de dejar tus objetos de valor desprotegidos.

Salida:

Alice
Bob
Age must be positive!
Charlie
150

Un guion bajo _attribute significa «solo para uso interno» por convención. El doble guion bajo __attribute activa la alteración de nombres para ofrecer mayor privacidad. Usa métodos getter/setter (métodos de acceso) para interactuar correctamente con los datos privados y añadir validación. El «código adicional» evita errores al garantizar que los datos siempre sean válidos.

challenge icon

Desafío

Fácil

Completa la clase User en user.py con la funcionalidad de contraseña privada y después úsala en driver.py. Sigue los comentarios TODO para implementar los métodos de comprobación y cambio de contraseña.

Pruébalo tú mismo

# TODO: Importa la clase User del módulo user

# TODO: Obtén el caso de prueba de la entrada
test_case = input()

if test_case == "constructor_test":
    # TODO: Crea un objeto User con la contraseña inicial "secure123"
    user = User("secure123")
    print("User created successfully")
    print("Initial password set")

elif test_case == "correct_password_test":
    # TODO: Crea un objeto User con la contraseña inicial "secure123"
    user = User("secure123")
    # TODO: Prueba el método check_password con la contraseña correcta
    #       Imprime "Password check successful!" si devuelve True
    #       Imprime "Password check failed!" si devuelve False
    result = user.check_password("secure123")
    if result:
        print("Password check successful!")
    else:
        print("Password check failed!")

elif test_case == "incorrect_password_test":
    # TODO: Crea un objeto User con la contraseña inicial "secure123"
    user = User("secure123")
    # TODO: Prueba el método check_password con la contraseña incorrecta "wrong"
    #       Imprime "Incorrect password rejected!" si devuelve False
    #       Imprime "Security issue: incorrect password accepted!" si devuelve True
    result = user.check_password("wrong")
    if not result:
        print("Incorrect password rejected!")
    else:
        print("Security issue: incorrect password accepted!")

elif test_case == "change_password_test":
    # TODO: Crea un objeto User con la contraseña inicial "secure123"
    user = User("secure123")
    # TODO: Prueba a cambiar la contraseña de "secure123" a "newpass456"
    #       Imprime "Password changed successfully!" si devuelve True
    #       Imprime "Password change failed!" si devuelve False
    result = user.change_password("secure123", "newpass456")
    if result:
        print("Password changed successfully!")
    else:
        print("Password change failed!")
    
    # TODO: Verifica que la nueva contraseña funcione comprobando "newpass456"
    #       Imprime "New password works!" si devuelve True
    #       Imprime "New password doesn't work!" si devuelve False
    if user.check_password("newpass456"):
        print("New password works!")
    else:
        print("New password doesn't work!")

elif test_case == "change_password_wrong_old_test":
    # TODO: Crea un objeto User con la contraseña inicial "secure123"
    user = User("secure123")
    # TODO: Intenta cambiar la contraseña con una contraseña anterior incorrecta
    #       Imprime "Security working: incorrect old password rejected!" si devuelve False
    #       Imprime "Security issue: password changed with wrong old password!" si devuelve True
    result = user.change_password("wrong", "hackerpw")
    if not result:
        print("Security working: incorrect old password rejected!")
    else:
        print("Security issue: password changed with wrong old password!")

elif test_case == "comprehensive_test":
    # TODO: Crea un objeto User con la contraseña inicial "secure123"
    user = User("secure123")
    print("User created with initial password")
    
    # TODO: Prueba el método check_password con la contraseña correcta
    if user.check_password("secure123"):
        print("Initial password verification successful")
    
    # TODO: Prueba el método check_password con una contraseña incorrecta
    if not user.check_password("wrongpass"):
        print("Incorrect password properly rejected")
    
    # TODO: Prueba a cambiar la contraseña de "secure123" a "newpass456"
    if user.change_password("secure123", "newpass456"):
        print("Password successfully changed")
    
    # TODO: Verifica que la contraseña anterior ya no funcione
    if not user.check_password("secure123"):
        print("Old password no longer works")
    
    # TODO: Verifica que la nueva contraseña funcione
    if user.check_password("newpass456"):
        print("New password works correctly")
    
    # TODO: Intenta cambiar la contraseña con una contraseña anterior incorrecta
    if not user.change_password("wrongold", "hackerpw"):
        print("Security maintained: wrong old password rejected")
    
    # TODO: Verifica que la contraseña siga siendo segura después de un intento fallido de cambio
    if user.check_password("newpass456"):
        print("Password remains secure after failed change attempt")

else:
    print("Unknown test case")
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