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ętrznegoDostę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 zalecaneUż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 imieniaset_age(age)— sprawdza wiek przed zezwoleniem na zmiany (żadnego ujemnego wieku!)- Bez tych metod ktoś mógłby ustawić
age = -100i 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) # CharliePrzykł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
150Pojedynczy 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.
Wyzwanie
ŁatwyUzupeł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")Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrzneWprowadzenie do programowania obiektowegoKlasy a obiektyParametr selfMetodyAtrybutyMetoda konstruktora (__init__)Powtórzenie – prosty kalkulator4Dziedziczenie
Podstawy dziedziczeniaFunkcja super()Nadpisywanie metodDziedziczenie wielokrotneKolejność rozwiązywania metodPodsumowanie – hierarchia pracowników7Metody specjalne
Wprowadzenie do metod magicznychPrzeciążanie operatorówMagiczne metody kontenerówPowtórka – własna lista10Wzorce projektowe, część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonWzorzec FactoryWzorzec ObserverWzorzec Strategy2Dekoratory
Wprowadzenie do dekoratorówDekorator propertyDekorator metody statycznejDekorator metody klasowej5Polimorfizm
Ponowne omówienie nadpisywania metodTypowanie kaczeKlasy i metody abstrakcyjneProjektowanie interfejsówPodsumowanie — kalkulator kształtów8Zaawansowane koncepcje programowania obiektowego
Kompozycja a dziedziczenieMiksyMetody statyczne i klasoweDekoratory klasMenedżery kontekstu11Wzorce projektowe, część 2
Wzorzec PolecenieWzorzec AdapterWzorzec DekoratorWzorzec Metoda szablonowaWzorzec StanWzorzec Kompozyt3Właściwości klas
Zmienne instancji a zmienne klasoweDekoratory właściwościAtrybuty prywatnePowtórzenie — menedżer kont bankowych6Hermetyzacja
Public, Protected, Private — pamięćModyfikatory dostępuUkrywanie informacjiDekoratory właściwości — zaawansowanePodsumowanie — system ewidencji uczniów12Projekt: System zarządzania biblioteką
Przegląd projektuKlasy Book i UserSystem wypożyczeńFunkcja wyszukiwaniaInterfejs administratoraTestowanie i integracjaPoćwicz samodzielnie: Kompilator Python online