Menu
Coddy logo textTech

Atrybuty prywatne

Część sekcji Programowanie obiektowe ścieżki Python w Coddy. Lekcja 15 z 64.

Atrybuty prywatne używają podkreśleń, aby wskazać, że niektóre dane nie powinny być dostępne bezpośrednio spoza klasy.

Dlaczego potrzebujemy atrybutów prywatnych? Wyobraź sobie, że masz konto bankowe. Nie chcesz, aby ktokolwiek bezpośrednio zmieniał Twoje saldo — powinien korzystać z odpowiednich kanałów (metod wpłaty/wypłaty), aby bank mógł zweryfikować transakcję, sprawdzić, czy nie doszło do oszustwa, prowadzić rejestry itp. Atrybuty prywatne działają tak samo — chronią Twoje dane przed nieprawidłowymi zmianami.

Oto przykład użycia pojedynczego podkreślenia (konwencja oznaczająca „do użytku wewnętrznego”):

class Person:
    def __init__(self, name, age):
        self._name = name    # "protected" - do użytku wewnętrznego
        self._age = age      # "protected" - do użytku wewnętrznego

Dostęp do atrybutów z pojedynczym podkreśleniem nadal jest możliwy, ale to sygnał, żeby tego nie robić:

person = Person("Alice", 30)
print(person._name)  # Działa, ale nie jest zalecane

Użyj podwójnych podkreśleń, aby zapewnić większą prywatność (mangling nazw):

class Person:
    def __init__(self, name, age):
        self.__name = name   # "private" - podlega modyfikacji nazwy
        self.__age = age     # "private" - podlega modyfikacji nazwy
    
    def get_name(self):
        return self.__name
    
    def set_age(self, age):
        if age >= 0:
            self.__age = age
        else:
            print("Age must be positive!")

Dlaczego używać metod pobierających i ustawiających? Są jak strażnicy. Zamiast pozwalać każdemu bezpośrednio zmieniać __age (co mogłoby skutkować ujemnym wiekiem lub innymi nieprawidłowymi danymi), zmuszamy do używania set_age(), która najpierw sprawdza dane wejściowe. Zapobiega to błędom i zapewnia integralność danych.

„Dodatkowy kod” ma swój cel:

  • get_name() — kontrolowany dostęp do odczytu imienia
  • set_age(age) — sprawdza wiek przed zezwoleniem na zmiany (żadnego ujemnego wieku!)
  • Bez tych metod ktoś mógłby ustawić age = -100 i zepsuć Twój program

Użyj metod dostępu, aby wchodzić w interakcje z prywatnymi atrybutami:

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

person.set_age(30)        # Poprawne: wiek zmienia się na 30
person.set_age(-5)        # Niepoprawne: Wiek musi być dodatni! (wiek pozostaje równy 30)

Widzisz korzyść? Metoda set_age() zapobiega nieprawidłowym danym. Bez niej możesz przypadkowo utworzyć osobę w wieku -5, co nie ma sensu!

Atrybuty z podwójnym podkreśleniem są poddawane „manglowaniu nazw”, ale nadal można uzyskać do nich dostęp:

person = Person("Charlie", 35)
# To nie działa:
# print(person.__name)  # AttributeError

# Ale to działa (niezalecane):
print(person._Person__name)  # Charlie

Przykład z życia wzięty — po co cały ten kod?

class BankAccount:
    def __init__(self, balance):
        self.__balance = balance  # Prywatne: nie można zmienić bezpośrednio
    
    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
            return True
        return False
    
    def get_balance(self):
        return self.__balance

# Bez prywatnych atrybutów:
# account.__balance = -1000000  # Katastrofa! Dozwolone saldo ujemne

# Z prywatnymi atrybutami:
account = BankAccount(100)
account.deposit(50)           # Bezpieczne: sprawdzone
print(account.get_balance())  # 150

„Dodatkowy kod” (metody) chroni Twoje dane przed nieprawidłowymi zmianami. To tak, jakby mieć ochroniarzy zamiast pozostawiać swoje kosztowności bez ochrony.

Wynik:

Alice
Bob
Age must be positive!
Charlie
150

Pojedynczy podkreślnik _attribute oznacza umownie „tylko do użytku wewnętrznego”. Podwójny podkreślnik __attribute uruchamia zmianę nazwy, aby zapewnić większą prywatność. Używaj metod pobierających i ustawiających (metod dostępowych), aby prawidłowo korzystać z prywatnych danych i dodawać walidację. „Dodatkowy kod” zapobiega błędom, zapewniając, że dane są zawsze prawidłowe.

challenge icon

Wyzwanie

Łatwy

Uzupełnij klasę User w pliku user.py o funkcjonalność prywatnego hasła, a następnie użyj jej w pliku driver.py. Postępuj zgodnie z komentarzami TODO, aby zaimplementować metody sprawdzania i zmieniania hasła.

Spróbuj swoich sił

# TODO: Zaimportuj klasę User z modułu user

# TODO: Pobierz przypadek testowy z danych wejściowych
test_case = input()

if test_case == "constructor_test":
    # TODO: Utwórz obiekt User z początkowym hasłem "secure123"
    user = User("secure123")
    print("User created successfully")
    print("Initial password set")

elif test_case == "correct_password_test":
    # TODO: Utwórz obiekt User z początkowym hasłem "secure123"
    user = User("secure123")
    # TODO: Przetestuj metodę check_password z poprawnym hasłem
    #       Wypisz "Password check successful!", jeśli metoda zwróci True
    #       Wypisz "Password check failed!", jeśli metoda zwróci False
    result = user.check_password("secure123")
    if result:
        print("Password check successful!")
    else:
        print("Password check failed!")

elif test_case == "incorrect_password_test":
    # TODO: Utwórz obiekt User z początkowym hasłem "secure123"
    user = User("secure123")
    # TODO: Przetestuj metodę check_password z niepoprawnym hasłem "wrong"
    #       Wypisz "Incorrect password rejected!", jeśli metoda zwróci False
    #       Wypisz "Security issue: incorrect password accepted!", jeśli metoda zwróci 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: Utwórz obiekt User z początkowym hasłem "secure123"
    user = User("secure123")
    # TODO: Przetestuj zmianę hasła z "secure123" na "newpass456"
    #       Wypisz "Password changed successfully!", jeśli metoda zwróci True
    #       Wypisz "Password change failed!", jeśli metoda zwróci False
    result = user.change_password("secure123", "newpass456")
    if result:
        print("Password changed successfully!")
    else:
        print("Password change failed!")
    
    # TODO: Sprawdź, czy nowe hasło działa, używając "newpass456"
    #       Wypisz "New password works!", jeśli metoda zwróci True
    #       Wypisz "New password doesn't work!", jeśli zwróci 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: Utwórz obiekt User z początkowym hasłem "secure123"
    user = User("secure123")
    # TODO: Spróbuj zmienić hasło, podając nieprawidłowe stare hasło
    #       Wypisz "Security working: incorrect old password rejected!", jeśli metoda zwróci False
    #       Wypisz "Security issue: password changed with wrong old password!", jeśli zwróci 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: Utwórz obiekt User z początkowym hasłem "secure123"
    user = User("secure123")
    print("User created with initial password")
    
    # TODO: Przetestuj metodę check_password z poprawnym hasłem
    if user.check_password("secure123"):
        print("Initial password verification successful")
    
    # TODO: Przetestuj metodę check_password z nieprawidłowym hasłem
    if not user.check_password("wrongpass"):
        print("Incorrect password properly rejected")
    
    # TODO: Przetestuj zmianę hasła z "secure123" na "newpass456"
    if user.change_password("secure123", "newpass456"):
        print("Password successfully changed")
    
    # TODO: Sprawdź, czy stare hasło już nie działa
    if not user.check_password("secure123"):
        print("Old password no longer works")
    
    # TODO: Sprawdź, czy nowe hasło działa
    if user.check_password("newpass456"):
        print("New password works correctly")
    
    # TODO: Spróbuj zmienić hasło, podając nieprawidłowe stare hasło
    if not user.change_password("wrongold", "hackerpw"):
        print("Security maintained: wrong old password rejected")
    
    # TODO: Sprawdź, czy hasło nadal jest bezpieczne po nieudanej próbie zmiany
    if user.check_password("newpass456"):
        print("Password remains secure after failed change attempt")

else:
    print("Unknown test case")
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator Python online