Menu
Coddy logo textTech

Atributos Privados

Parte da seção Object Oriented Programming do Journey de Python da Coddy — lição 15 de 64.

Atributos privados usam sublinhados para indicar que certos dados não devem ser acessados diretamente de fora da classe.

Por que precisamos de atributos privados? Imagine que você tem uma conta bancária. Você não quer que ninguém altere diretamente o seu saldo - eles devem passar pelos canais adequados (métodos deposit/withdraw) para que o banco possa validar a transação, verificar fraudes, manter registros, etc. Atributos privados funcionam da mesma maneira - eles protegem seus dados de serem alterados incorretamente.

Aqui está um exemplo usando um único sublinhado (convenção para "uso interno"):

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

Atributos com sublinhado único ainda podem ser acessados, mas é um sinal para não fazê-lo:

person = Person("Alice", 30)
print(person._name)  # Funciona, mas não é recomendado

Use sublinhados duplos para maior privacidade (name mangling):

class Person:
    def __init__(self, name, age):
        self.__name = name   # "privado" - sofre name mangling
        self.__age = age     # "privado" - sofre name mangling
    
    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 que usar métodos getter e setter? Eles são como guardiões. Em vez de permitir que qualquer pessoa altere __age diretamente (o que poderia resultar em idades negativas ou outros dados inválidos), nós os forçamos a usar set_age(), que valida a entrada primeiro. Isso evita bugs e garante a integridade dos dados.

O "código extra" tem um propósito:

  • get_name() - Acesso controlado para ler o nome
  • set_age(age) - Valida a idade antes de permitir alterações (sem idades negativas!)
  • Sem esses métodos, alguém poderia definir age = -100 e quebrar seu programa

Use os métodos acessores para interagir com atributos privados:

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

person.set_age(30)        # Válido: a idade torna-se 30
person.set_age(-5)        # Inválido: A idade deve ser positiva! (a idade permanece 30)

Viu o benefício? O método set_age() evita dados inválidos. Sem ele, você poderia acidentalmente criar uma pessoa com idade -5, o que não faz sentido!

Atributos com sublinhado duplo sofrem "name mangling", mas ainda podem ser acessados:

person = Person("Charlie", 35)
# Isso não funciona:
# print(person.__name)  # AttributeError

# Mas isso funciona (desencorajado):
print(person._Person__name)  # Charlie

Exemplo do mundo real - Por que todo esse código?

class BankAccount:
    def __init__(self, balance):
        self.__balance = balance  # Privado: não pode ser alterado diretamente
    
    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
            return True
        return False
    
    def get_balance(self):
        return self.__balance

# Sem atributos privados:
# account.__balance = -1000000  # Desastre! Saldo negativo permitido

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

O "código extra" (métodos) protege seus dados de alterações inválidas. É como ter seguranças em vez de deixar seus objetos de valor desprotegidos.

Saída:

Alice
Bob
Age must be positive!
Charlie
150

Um único sublinhado _attribute significa "apenas para uso interno" por convenção. O sublinhado duplo __attribute aciona o name mangling para uma privacidade mais forte. Use métodos getter/setter (métodos de acesso) para interagir adequadamente com dados privados e adicionar validação. O "código extra" evita bugs ao garantir que os dados sejam sempre válidos.

challenge icon

Desafio

Fácil

Complete a classe User em user.py com funcionalidade de senha privada e, em seguida, use-a em driver.py. Siga os comentários TODO para implementar os métodos de verificação e alteração de senha.

Folha de consulta

Atributos privados usam sublinhados para indicar que os dados não devem ser acessados diretamente de fora da classe.

Sublinhado único (_attribute) - convenção para "uso interno":

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

Sublinhado duplo (__attribute) - aciona a desfiguração de nomes (name mangling) para maior privacidade:

class Person:
    def __init__(self, name, age):
        self.__name = name   # "privado" - sofre desfiguração de nome
        self.__age = age     # "privado" - sofre desfiguração de nome
    
    def get_name(self):
        return self.__name
    
    def set_age(self, age):
        if age >= 0:
            self.__age = age
        else:
            print("Age must be positive!")

Use métodos de acesso para interagir com atributos privados:

person = Person("Bob", 25)
print(person.get_name())  # Bob
person.set_age(30)
person.set_age(-5)        # Age must be positive!

Atributos com sublinhado duplo sofrem desfiguração de nome, mas ainda podem ser acessados (desaconselhado):

# Isso não funciona:
# print(person.__name)  # AttributeError

# Mas isso funciona (desaconselhado):
print(person._Person__name)  # Charlie

Experimente você mesmo

# TODO: Importar a classe User do módulo user

# TODO: Obter o caso de teste da entrada
test_case = input()

if test_case == "constructor_test":
    # TODO: Criar um objeto User com a senha inicial "secure123"
    user = User("secure123")
    print("User created successfully")
    print("Initial password set")

elif test_case == "correct_password_test":
    # TODO: Criar um objeto User com a senha inicial "secure123"
    user = User("secure123")
    # TODO: Testar o método check_password com a senha correta
    #       Imprimir "Password check successful!" se retornar True
    #       Imprimir "Password check failed!" se retornar False
    result = user.check_password("secure123")
    if result:
        print("Password check successful!")
    else:
        print("Password check failed!")

elif test_case == "incorrect_password_test":
    # TODO: Criar um objeto User com a senha inicial "secure123"
    user = User("secure123")
    # TODO: Testar o método check_password com a senha incorreta "wrong"
    #       Imprimir "Incorrect password rejected!" se retornar False
    #       Imprimir "Security issue: incorrect password accepted!" se retornar 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: Criar um objeto User com a senha inicial "secure123"
    user = User("secure123")
    # TODO: Testar a alteração da senha de "secure123" para "newpass456"
    #       Imprimir "Password changed successfully!" se retornar True
    #       Imprimir "Password change failed!" se retornar False
    result = user.change_password("secure123", "newpass456")
    if result:
        print("Password changed successfully!")
    else:
        print("Password change failed!")
    
    # TODO: Verificar se a nova senha funciona testando "newpass456"
    #       Imprimir "New password works!" se retornar True
    #       Imprimir "New password doesn't work!" se retornar 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: Criar um objeto User com a senha inicial "secure123"
    user = User("secure123")
    # TODO: Tentar alterar a senha com a senha antiga incorreta
    #       Imprimir "Security working: incorrect old password rejected!" se retornar False
    #       Imprimir "Security issue: password changed with wrong old password!" se retornar 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: Criar um objeto User com a senha inicial "secure123"
    user = User("secure123")
    print("User created with initial password")
    
    # TODO: Testar o método check_password com a senha correta
    if user.check_password("secure123"):
        print("Initial password verification successful")
    
    # TODO: Testar o método check_password com a senha incorreta
    if not user.check_password("wrongpass"):
        print("Incorrect password properly rejected")
    
    # TODO: Testar a alteração da senha de "secure123" para "newpass456"
    if user.change_password("secure123", "newpass456"):
        print("Password successfully changed")
    
    # TODO: Verificar se a senha antiga não funciona mais
    if not user.check_password("secure123"):
        print("Old password no longer works")
    
    # TODO: Verificar se a nova senha funciona
    if user.check_password("newpass456"):
        print("New password works correctly")
    
    # TODO: Tentar alterar a senha com a senha antiga incorreta
    if not user.change_password("wrongold", "hackerpw"):
        print("Security maintained: wrong old password rejected")
    
    # TODO: Verificar se a senha ainda está segura após tentativa de alteração malsucedida
    if user.check_password("newpass456"):
        print("Password remains secure after failed change attempt")

else:
    print("Unknown test case")
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Object Oriented Programming