Spis zadań w tym module

  1. Ochrona wieku użytkownika (Walidacja przez setter)
  2. Bezpieczne saldo konta (Zmienne chronione _)
  3. Właściwości wyliczane w geometrii (ReadOnly property)
  4. Inteligentny termometr (Automatyczna konwersja jednostek)
  5. Zarządzanie hasłem dostępu (Maskowanie danych)

1. Kontrola dostępu do danych

Domyślnie wszystkie atrybuty w Pythonie są publiczne. Hermetyzacja (enkapsulacja) to mechanizm ukrywania wewnętrznego stanu obiektu przed bezpośrednią modyfikacją. Dzięki temu chronimy logikę biznesową programu przed błędnymi danymi.

2. Konwencja podkreślnika

Atrybut z pojedynczym podkreślnikiem (np. _saldo) jest uznawany za chroniony. To sygnał dla programistów: "To jest detal implementacji, nie zmieniaj go bezpośrednio". Z kolei podwójny podkreślnik (np. __sekret) uruchamia mechanizm "name mangling", który utrudnia dostęp do atrybutu z zewnątrz klasy.

01
Ochrona wieku użytkownika (Walidacja przez setter)
Czego student się nauczy

Stosowania dekoratora @property do odczytu danych oraz @nazwa.setter do ich bezpiecznego zapisu z walidacją.

Scenariusz

Projektujesz moduł rejestracji dla nowego, ogólnoświatowego portalu społecznościowego, który musi przestrzegać rygorystycznych zasad ochrony danych osobowych. Jednym z fundamentów bezpieczeństwa jest zapewnienie, aby wiek użytkowników mieścił się w logicznych i dopuszczalnych granicach biologicznych. Twoim zadaniem jest stworzenie klasy, która uniemożliwi przypadkowe lub celowe wprowadzenie ujemnych wartości bądź nierealistycznie wysokich liczb. Zastosuj mechanizm właściwości, aby ukryć wewnętrzną reprezentację wieku i wystawić go na zewnątrz w sposób kontrolowany. Każda próba modyfikacji tej wartości musi zostać poddana walidacji przez dedykowany setter, który sprawdzi czy podana liczba mieści się w zakresie od zera do stu pięćdziesięciu lat. W przypadku wykrycia nieprawidłowości, system powinien zablokować zmianę i poinformować programistę o zaistniałym błędzie za pomocą czytelnego komunikatu. Takie podejście gwarantuje spójność danych w całej bazie i chroni aplikację przed błędami logicznymi w przyszłości. Dzięki hermetyzacji, interfejs Twojej klasy pozostanie prosty i intuicyjny, pomimo skomplikowanych reguł działających w tle.

Wymagania techniczne
  • Zdefiniuj klasę Uzytkownik posiadającą wewnętrzny atrybut instancji _wiek (z pojedynczym podkreślnikiem).
  • Zaimplementuj metodę dekorowaną @property o nazwie wiek do odczytu wartości atrybutu.
  • Opracuj metodę typu setter o nazwie @wiek.setter do kontrolowanej modyfikacji wieku.
  • Dodaj wewnątrz settera rygorystyczną walidację sprawdzającą zakres liczbowy od 0 do 150 lat.
  • Zaimplementuj blokadę przypisywania wartości nielogicznych, takich jak liczby ujemne.
  • Wyświetl czytelny komunikat błędu (ostrzeżenie) w konsoli, gdy walidacja zakończy się niepowodzeniem.
  • Upewnij się, że w przypadku błędu poprzednia wartość wieku pozostaje niezmieniona.
  • Przetestuj działanie właściwości, próbując ustawić wiek poprawny oraz wartości spoza dozwolonego zakresu.
Wskazówki wykonania
  • Rozpocznij od zdefiniowania atrybutu self._wiek w konstruktorze __init__.
  • Użyj dekoratora @property nad metodą o nazwie wiek(self), która zwraca wartość self._wiek.
  • Bezpośrednio pod getterem dodaj setter: @wiek.setter dla tej samej nazwy metody.
  • Wewnątrz settera użyj instrukcji warunkowej if 0 <= nowa_wartosc <= 150:.
  • Pamiętaj, aby przypisanie self._wiek = nowa_wartosc nastąpiło tylko po pozytywnej walidacji.
  • Jeśli wartość jest błędna, użyj funkcji print(), aby wyświetlić ostrzeżenie użytkownikowi.
  • Przetestuj kod, tworząc obiekt: u = Uzytkownik(20).
  • Spróbuj przypisać wartość u.wiek = -10 i sprawdź, czy stan obiektu nie uległ zmianie.
  • Zwróć uwagę, że z zewnątrz odwołujesz się do u.wiek, a nie do wewnętrznego u._wiek.
  • Dzięki temu podejściu, logika sprawdzająca jest całkowicie ukryta przed użytkownikiem klasy.
Przykładowy ekran
>>> u = Uzytkownik(25) >>> u.wiek = 30 >>> print(u.wiek) 30 >>> u.wiek = -5 Błąd: Wiek musi być z zakresu 0-150! >>> u.wiek = 200 Błąd: Wiek musi być z zakresu 0-150!
Wnioski do opracowania
  • Wyjaśnij, dlaczego użycie intuicyjnej składni kropkowej (np. u.wiek = 30) jest bardziej ergonomiczne dla programisty niż wywoływanie metod typu u.set_wiek(30).
  • Opisz fundamentalną rolę dekoratora @property jako narzędzia do mapowania logiki metody na atrybut odczytywalny przez systemy zewnętrzne.
  • Omów mechanizm walidacji zaszyty wewnątrz settera jako skuteczny sposób na wymuszanie poprawności logiki biznesowej już na poziomie zapisu.
  • Przeanalizuj skutki intencjonalnego braku settera dla konkretnej właściwości – co to oznacza w praktyce dla bezpieczeństwa i stałości danych?
  • Zastanów się i opisz, jak hermetyzacja ułatwia późniejszą rozbudowę klasy o dodatkowe funkcjonalności bez konieczności modyfikowania kodu w innych modułach.
  • Wnioskuj o znaczeniu konwencji pojedynczego podkreślnika (_wiek) dla poprawnej komunikacji i współpracy w zespole programistycznym.
  • Porównaj elastyczne podejście Pythona do hermetyzacji z rygorystycznymi modyfikatorami dostępu (private, protected) znanymi z języków takich jak Java czy C++.
  • Opisz, w jaki sposób system powinien profesjonalnie reagować na próby wprowadzenia danych typu niezgodnego z oczekiwaniami (np. tekst zamiast liczby).
  • Sprawdź i potwierdź na przykładach, czy stan wewnętrzny obiektu pozostaje spójny po serii celowo błędnych prób modyfikacji wieku użytkownika.

Rozwiązanie

3. Nowoczesne podejście (Properties)

Dekorator @property pozwala zamienić metodę w coś, co z zewnątrz wygląda jak zwykły atrybut. Pozwala to na dodanie logiki (np. walidacji) do istniejącego już kodu bez konieczności zmiany sposobu, w jaki inni programiści odwołują się do Twoich danych.

02
Bezpieczne saldo konta (Zmienne chronione _)
Czego student się nauczy

Praktycznego wykorzystania konwencji podkreślnika do ochrony stanu wewnętrznego oraz projektowania bezpiecznego interfejsu (API) klasy.

Scenariusz

W ramach prac nad bezpiecznym rdzeniem systemu bankowości elektronicznej, musisz zadbać o najwyższy poziom ochrony stanu posiadania klientów. Bezpośrednia manipulacja saldem konta jest niedopuszczalna, gdyż mogłaby prowadzić do nieautoryzowanych zmian i oszustw finansowych. Twoim celem jest zaprojektowanie klasy, która wykorzystuje zmienne chronione do przechowywania aktualnej kwoty pieniędzy na rachunku. Dostęp do odczytu stanu konta powinien być realizowany poprzez bezpieczną właściwość, która nie pozwala na bezpośrednie przypisanie nowej wartości za pomocą operatora równości. Wszelkie operacje finansowe, takie jak wpłaty czy wypłaty, muszą odbywać się wyłącznie przez dedykowane, autoryzowane metody przechodzące przez system weryfikacji. Dzięki temu rozwiązaniu, saldo pozostaje pod stałą kontrolą logiki biznesowej, a każda zmiana jest wynikiem konkretnej akcji użytkownika. Takie podejście do hermetyzacji jest standardem w profesjonalnych aplikacjach finansowych, gdzie integralność danych jest absolutnym priorytetem. Poprawnie zaimplementowany model zabezpieczy Twoich klientów przed błędami programistycznymi i zwiększy zaufanie do całego systemu.

Wymagania techniczne
  • Utwórz klasę Konto z chronionym atrybutem instancji _saldo definiowanym w konstruktorze.
  • Zaimplementuj właściwość @property o nazwie saldo umożliwiającą wyłącznie odczyt stanu konta.
  • Celowo pomiń definicję settera dla właściwości saldo, aby uniemożliwić bezpośrednie przypisanie wartości.
  • Stwórz publiczną metodę depozyt(kwota) do bezpiecznego zwiększania stanu oszczędności.
  • Opracuj metodę wyplata(kwota) do realizowania transakcji wychodzących z rachunku.
  • Dodaj wewnątrz metody wypłaty warunek sprawdzający, czy dostępne środki są wystarczające.
  • Sprawdź działanie ochrony danych, próbując nadpisać saldo przy użyciu operatora przypisania =.
  • Przechwyć i opisz błąd AttributeError, który powinien wystąpić przy próbie nielegalnej modyfikacji.
Wskazówki wykonania
  • Zdefiniuj atrybut self._saldo, aby zasygnalizować, że jest to pole chronione (internal).
  • Stwórz metodę saldo(self) z dekoratorem @property, która tylko zwraca wartość tego atrybutu.
  • Celowo nie twórz metody @saldo.setter, co uczyni saldo atrybutem tylko do odczytu z zewnątrz.
  • Metoda depozyt(self, kwota) powinna bezpośrednio modyfikować wartość self._saldo.
  • W metodzie wyplata(self, kwota) dodaj warunek bezpieczeństwa: if kwota <= self._saldo:.
  • Przy testowaniu spróbuj wykonać konto.saldo = 1000 i zaobserwuj błąd AttributeError.
  • Pamiętaj, że brak settera to najprostszy sposób na stworzenie bezpiecznych parametrów obiektu.
  • Używaj f-stringów do raportowania stanu konta po każdej zakończonej operacji finansowej.
  • Przetestuj serię wypłat, kontrolując stan konta wyłącznie za pomocą właściwości saldo.
  • Zastanów się, dlaczego takie podejście drastycznie zwiększa bezpieczeństwo aplikacji bankowej.
Przykładowy ekran
>>> konto = Konto(100) >>> print(konto.saldo) 100 >>> konto.saldo = 500 AttributeError: can't set attribute >>> konto.depozyt(200) >>> print(konto.saldo) 300
Wnioski do opracowania
  • Wyjaśnij precyzyjnie pojęcie "atrybutu tylko do odczytu" (Read-only) i wskaż konkretne sytuacje biznesowe, w których jest ono niezbędne.
  • Opisz, w jaki sposób techniczny brak settera dla właściwości saldo skutecznie chroni integralność krytycznych danych finansowych.
  • Omów zalety delegowania wszelkich zmian stanu obiektu wyłącznie do autoryzowanych metod o określonej logice (np. depozyt lub wyplata).
  • Przeanalizuj standardowy komunikat błędu AttributeError jako jasny sygnał dla programisty o naruszeniu zdefiniowanych zasad dostępu do danych.
  • Zastanów się nad scenariuszem, w którym saldo musi być publicznie odczytywalne, ale kategorycznie nigdy nie modyfikowalne bezpośrednio przez operator przypisania.
  • Wnioskuj o różnicy między zwykłą zmienną publiczną a właściwością bez zdefiniowanego settera w kontekście projektowania bezpiecznego API klasy.
  • Porównaj poziom bezpieczeństwa kodu wykorzystującego konwencję podkreślnika (_saldo) do kodu z całkowicie jawnymi i niezabezpieczonymi polami.
  • Opisz znaczenie wykorzystania f-stringów w precyzyjnym raportowaniu stanu konta po każdej pomyślnie lub niepomyślnie zakończonej operacji finansowej.
  • Sprawdź, czy próba celowego "obejścia" właściwości przez odwołanie do _saldo jest technicznie możliwa i wyjaśnij, dlaczego jest to praktyka odradzana.

Rozwiązanie

4. Właściwości wyliczane

Property nie musi zwracać wartości zmiennej. Może obliczać wynik "w locie" na podstawie innych atrybutów. Dzięki temu mamy pewność, że wynik jest zawsze aktualny (np. pole powierzchni po zmianie długości boku).

03
Właściwości wyliczane w geometrii (ReadOnly property)
Czego student się nauczy

Tworzenia atrybutów wirtualnych, które nie przechowują danych, lecz wyliczają je dynamicznie przy każdym dostępie.

Scenariusz

Jako programista silnika do obliczeń inżynierskich, musisz stworzyć zestaw narzędzi do pracy z prostokątnymi elementami konstrukcyjnymi. Tradycyjne podejście polegające na przechowywaniu pola powierzchni jako statycznej zmiennej często prowadzi do błędów, gdy zapomnimy o jej aktualizacji po zmianie długości boków. Twoim zadaniem jest wyeliminowanie tego ryzyka poprzez zastosowanie właściwości wyliczanych dynamicznie przy każdym dostępie do danych. Klasa powinna pozwalać na swobodną zmianę wymiarów podstawy i wysokości, gwarantując, że wynikowa informacja o powierzchni będzie zawsze idealnie spójna z aktualnym stanem obiektu. Wykorzystaj dekorator właściwości, aby umożliwić użytkownikowi odczyt pola powierzchni w taki sam sposób, w jaki odczytuje się zwykłe atrybuty instancji. Takie rozwiązanie ukrywa logikę matematyczną przed użytkownikiem końcowym i zapobiega powstawaniu tzw. "martwych danych" w pamięci programu. Dzięki temu podejściu, Twój moduł geometryczny będzie działał sprawnie i bezbłędnie, nawet w najbardziej złożonych symulacjach technicznych. Gotowa klasa stanie się doskonałym przykładem efektywnego wykorzystania właściwości tylko do odczytu w programowaniu obiektowym.

Wymagania techniczne
  • Zdefiniuj klasę Prostokat przyjmującą długości dwóch boków (a i b) w momencie tworzenia.
  • Pozostaw atrybuty boków jako publiczne, umożliwiając ich swobodną zmianę w dowolnym czasie.
  • Zaimplementuj właściwość @property o nazwie pole do dynamicznego wyliczania powierzchni.
  • Upewnij się, że metoda pole nie przechowuje wartości w zmiennej, lecz wykonuje mnożenie "w locie".
  • Gwarantuj pełną spójność danych: zmiana boku a lub b musi natychmiast wpłynąć na wynik właściwości pole.
  • Opracuj metodę info() prezentującą aktualne wymiary figury oraz jej wyliczone pole powierzchni.
  • Przetestuj zachowanie klasy, zmieniając długości boków i obserwując automatyczną aktualizację pola.
  • Wyjaśnij zalety takiego podejścia w kontekście unikania błędów synchronizacji stanu obiektu.
Wskazówki wykonania
  • W konstruktorze __init__ zainicjalizuj self.a oraz self.b jako zwykłe atrybuty publiczne.
  • Zdefiniuj metodę pole(self) i oznacz ją dekoratorem @property.
  • Metoda ta nie powinna korzystać z żadnego ukrytego atrybutu, lecz zwracać wynik mnożenia self.a * self.b.
  • Dzięki temu wywołanie print(obiekt.pole) zawsze poda wynik na podstawie aktualnych wymiarów.
  • Metoda info(self) może korzystać z właściwości pole wewnątrz f-stringa: f"Pole: {self.pole}".
  • Przetestuj zmianę wymiarów: p.a = 5, a następnie natychmiast sprawdź p.pole.
  • Zauważ, że nie musisz pamiętać o ręcznym przeliczaniu powierzchni po każdej zmianie boku figury.
  • To podejście całkowicie eliminuje ryzyko niespójności danych (tzw. stale data).
  • Pamiętaj, że właściwości wyliczane są idealne dla danych zależnych od innych parametrów klasy.
  • Upewnij się, że nie zdefiniowałeś settera dla pola, ponieważ powierzchnia jest wartością wynikową.
Przykładowy ekran
>>> p = Prostokat(4, 5) >>> print(p.pole) 20 >>> p.a = 10 >>> print(p.pole) 50
Wnioski do opracowania
  • Wyjaśnij szczegółowo, dlaczego dynamiczne wyliczanie pola powierzchni "w locie" jest bezpieczniejsze niż przechowywanie go w stałej zmiennej pomocniczej.
  • Opisz niebezpieczny problem "martwych danych" (stale data), który występuje w systemach przy braku automatycznej synchronizacji zależnych atrybutów.
  • Omów zalety stosowania dekoratora @property do tworzenia tzw. atrybutów wirtualnych, które nie zajmują dodatkowej pamięci operacyjnej.
  • Przeanalizuj, w jaki sposób mechanizm automatycznej aktualizacji pola powierzchni upraszcza codzienne korzystanie z klasy przez inżynierów i architektów.
  • Zaproponuj, jakie inne parametry geometryczne figury (np. obwód lub przekątna) mogłyby zostać zaimplementowane jako właściwości wyliczane.
  • Wnioskuj o poprawie ogólnej spójności modelu matematycznego w Twoim programie dzięki szerokiemu użyciu dynamicznych właściwości odczytywalnych.
  • Porównaj wydajność procesorową obliczeń wykonywanych "w locie" do statycznego przechowywania gotowych wyników przy rzadkich zmianach wymiarów obiektu.
  • Opisz, dlaczego właściwości wyliczane z definicji nie powinny posiadać setterów (skoro pole powierzchni zależy bezpośrednio od długości boków).
  • Sprawdź i opisz zachowanie klasy przy próbie przypisania bokom wartości ujemnych – jak właściwość pole powinna logicznie na to zareagować?

Rozwiązanie

5. Automatyczna synchronizacja danych

Możemy użyć setterów do synchronizacji wielu formatów danych. Na przykład ustawiając temperaturę w stopniach Celsjusza, automatycznie "aktualizujemy" jej reprezentację w innych skalach.

04
Inteligentny termometr (Automatyczna konwersja jednostek)
Czego student się nauczy

Implementacji logiki konwersji danych wewnątrz setterów oraz zarządzania spójnością różnych widoków tych samych danych.

Scenariusz

Pracujesz nad oprogramowaniem dla nowoczesnej stacji pogodowej, która ma za zadanie obsługiwać użytkowników z różnych kręgów kulturowych i regionów świata. Różnice w stosowanych skalach temperatury, takich jak Celsjusz i Fahrenheit, wymagają stworzenia systemu automatycznie synchronizującego te dane w czasie rzeczywistym. Twoim zadaniem jest zaprojektowanie inteligentnej klasy termometru, która wewnętrznie operuje na jednej, bazowej skali, ale pozwala na swobodną interakcję w dowolnej innej. Wykorzystaj settery i gettery do zaimplementowania logiki konwersji, która zadziała natychmiast po przypisaniu nowej wartości do wybranego atrybutu. Dzięki temu ustawienie temperatury w skali Fahrenheita automatycznie zaktualizuje wewnętrzny stan w stopniach Celsjusza, zachowując pełną spójność informacji. Takie podejście eliminuje konieczność ręcznego wywoływania metod przeliczających przez programistę korzystającego z Twojej biblioteki. System stanie się bardziej elastyczny i odporny na błędy wynikające z pominięcia kroków transformacji danych w kodzie aplikacji. Gotowe rozwiązanie pokazuje potęgę właściwości w zarządzaniu złożonymi relacjami między danymi składowymi obiektu.

Wymagania techniczne
  • Skonstruuj klasę Termometr z jednym bazowym atrybutem wewnętrznym _celsius.
  • Stwórz parę metod (getter i setter) dla właściwości celsius do bezpośredniej pracy w tej skali.
  • Zaimplementuj właściwość @property o nazwie fahrenheit do obsługi alternatywnej skali temperatur.
  • Opracuj getter dla fahrenheit, który automatycznie konwertuje stan wewnętrzny według wzoru: (C * 1.8) + 32.
  • Zaimplementuj setter dla fahrenheit, który przelicza wartość wejściową z powrotem na stopnie Celsjusza.
  • Zapewnij pełną synchronizację: zmiana temperatury w jednej skali musi być widoczna w drugiej.
  • Dodaj w setterze skali Celsjusza walidację blokującą temperatury poniżej zera absolutnego (-273.15 °C).
  • Przetestuj działanie termometru, ustawiając wartości w obu skalach i sprawdzając wyniki przeliczeń.
Wskazówki wykonania
  • Klasa powinna przechowywać bazowe dane w wewnętrznym atrybucie self._celsius.
  • Stwórz właściwość celsius z getterem i setterem, aby kontrolować dostęp do tej skali.
  • W setterze celsius dodaj walidację: if nowa_temp < -273.15: print("Błąd!").
  • Zdefiniuj drugą właściwość o nazwie fahrenheit (również getter i setter).
  • Getter fahrenheit powinien zwracać wynik działania: (self._celsius * 1.8) + 32.
  • Setter fahrenheit musi przeliczać wartość wejściową: self._celsius = (nowa_temp - 32) / 1.8.
  • Zwróć uwagę, że setter Fahrenheita pośrednio modyfikuje chroniony atrybut Celsjusza.
  • Przetestuj synchronizację: ustaw t.celsius = 0 i sprawdź t.fahrenheit (powinno być 32.0).
  • Przetestuj odwrotnie: ustaw t.fahrenheit = 100 i sprawdź automatyczną zmianę t.celsius.
  • Dzięki temu termometr zawsze pokazuje spójną temperaturę w obu skalach jednocześnie bez Twojej ingerencji.
Przykładowy ekran
>>> t = Termometr(0) >>> print(t.fahrenheit) 32.0 >>> t.fahrenheit = 100 >>> print(t.celsius) 37.77777777777778
Wnioski do opracowania
  • Wyjaśnij, w jaki sposób mechanizm właściwości pozwala na eleganckie ukrycie skomplikowanych konwersji i przeliczeń skal przed użytkownikiem końcowym klasy.
  • Opisz techniczny mechanizm "podwójnej synchronizacji" – w jaki sposób zmiana temperatury w jednej skali natychmiastowo aktualizuje widok w drugiej.
  • Omów kluczową rolę bazowego atrybutu wewnętrznego (np. _celsius) jako centralnego "punktu prawdy" (source of truth) dla całego obiektu.
  • Przeanalizuj zalety stosowania rygorystycznej walidacji w setterze bazy (skala Celsjusza) dla wszystkich innych skal zależnych od tej wartości.
  • Zaproponuj sposób rozbudowy Twojego termometru o skalę Kelvina, korzystając z tych samych sprawdzonych zasad hermetyzacji i właściwości.
  • Wnioskuj o całkowitej eliminacji błędów synchronizacji dzięki przeniesieniu całej logiki transformacji danych do chronionego wnętrza klasy.
  • Porównaj intuicyjny interfejs typu termometr.fahrenheit = 32 do tradycyjnego zestawu metod dostępowych typu set_temp_f(32).
  • Opisz znaczenie pojęcia zera absolutnego jako naturalnej granicy walidacyjnej w profesjonalnych systemach fizycznych i pomiarowych.
  • Sprawdź, czy ograniczona precyzja liczb zmiennoprzecinkowych (float) może wpływać na drobne błędy zaokrągleń przy wielokrotnych konwersjach między skalami.

Rozwiązanie

6. Name Mangling (__)

Użycie podwójnego podkreślnika powoduje, że Python zmienia nazwę atrybutu (dodaje nazwę klasy jako prefiks). Jest to najsilniejsza forma "prywatności" w Pythonie, stosowana gdy chcemy uniknąć konfliktów nazw w hierarchii klas.

7. Zasada "Consenting Adults"

W Pythonie nie ma rygorystycznych blokad dostępu (jak private w Javie). Język opiera się na zaufaniu i konwencjach. Programista może "włamać się" do chronionych danych, ale robi to na własną odpowiedzialność.

8. Deletery (Usuwanie właściwości)

Możemy również kontrolować moment usuwania atrybutu za pomocą dekoratora @nazwa.deleter, co pozwala np. na zwolnienie zasobów lub wyczyszczenie pamięci podręcznej.

9. Kompatybilność wsteczna

Jedną z największych zalet @property jest możliwość dodania walidacji do pola, które wcześniej było publiczne, bez konieczności zmiany kodu w aplikacjach, które już z tej klasy korzystają.

10. Podsumowanie Hermetyzacji

Nauczyłeś się, jak chronić dane przed nieprawidłowym użyciem. Wiesz już, jak budować inteligentne atrybuty, które potrafią same się walidować i przeliczać. Twoje klasy stały się bezpieczniejsze i bardziej profesjonalne.

05
Zarządzanie hasłem dostępu (Maskowanie danych)
Czego student się nauczy

Zaawansowanego wykorzystania właściwości do maskowania wrażliwych danych (security by design) oraz implementacji złożonej walidacji przy zapisie.

Scenariusz

W dobie rosnących zagrożeń cybernetycznych, ochrona haseł użytkowników jest jednym z najważniejszych wyzwań dla każdego programisty budującego systemy bezpieczeństwa. Twoim zadaniem jest stworzenie klasy, która realizuje zasadę maskowania wrażliwych informacji już na poziomie definicji obiektu. Nawet jeśli programista spróbuje wyświetlić hasło w konsoli, system powinien zawsze zwracać bezpieczny ciąg znaków, taki jak gwiazdki, zamiast jawnego tekstu. Dodatkowo musisz zaimplementować rygorystyczne zasady walidacji przy próbie ustawienia nowego hasła, wymuszając jego odpowiednią długość i złożoność. Wykorzystaj mechanizm podwójnego podkreślnika, aby maksymalnie utrudnić bezpośredni dostęp do surowych danych z zewnątrz klasy. Dzięki zastosowaniu dekoratorów właściwości, proces zmiany i weryfikacji hasła pozostanie przejrzysty, przy jednoczesnym zachowaniu najwyższych standardów ochrony. Takie podejście "security by design" sprawia, że Twoja aplikacja jest znacznie trudniejsza do złamania i chroni prywatność użytkowników w każdych warunkach. Poprawna implementacja tego zadania pozwoli Ci zrozumieć, jak chronić krytyczne zasoby systemowe przed nieautoryzowanym odczytem.

Wymagania techniczne
  • Zdefiniuj klasę KontoUzytkownika z prywatnym atrybutem __haslo wykorzystującym mechanizm name mangling.
  • Zaimplementuj właściwość @property o nazwie password do kontrolowanego dostępu do hasła.
  • Opracuj getter dla password, który zamiast jawnego hasła zawsze zwraca maskę tekstową (np. osiem gwiazdek).
  • Stwórz setter dla password odpowiedzialny za bezpieczną aktualizację tajnego klucza.
  • Dodaj wewnątrz settera walidację minimalnej długości hasła (np. minimum 8 znaków).
  • Wyświetl komunikat błędu i odrzuć nowe hasło, jeśli nie spełnia ono wymogów bezpieczeństwa.
  • Zweryfikuj działanie ochrony, próbując odczytać atrybut __haslo bezpośrednio z poziomu instancji.
  • Opisz sposób, w jaki Python zmienia nazwy prywatnych atrybutów, utrudniając ich nieumyślny odczyt.
  • Użyj podwójnego podkreślnika przy definicji atrybutu instancji: self.__haslo = haslo.
  • Pamiętaj, że podwójny podkreślnik uruchamia mechanizm name mangling wewnątrz interpretera Pythona.
  • Getter właściwości password powinien zawsze zwracać stały ciąg gwiazdek, np. return "********".
  • W setterze password sprawdź długość nowego hasła: if len(nowe_haslo) >= 8:.
  • Dopiero po spełnieniu tego wymogu zaktualizuj prywatny atrybut: self.__haslo = nowe_haslo.
  • Jeśli hasło jest za krótkie, wyświetl komunikat błędu i nie zmieniaj poprzednio zapisanego klucza.
  • Spróbuj odczytać hasło z zewnątrz używając u.__haslo i zobacz, że Python rzuci błąd braku atrybutu.
  • Aby zobaczyć jak Python faktycznie ukrył zmienną, użyj polecenia print(u.__dict__) lub dir(u).
  • Zauważysz tam atrybut o specyficznej nazwie _KontoUzytkownika__haslo.
  • To zadanie uczy, że hermetyzacja to nie tylko walidacja, ale też świadome maskowanie danych wrażliwych.
Przykładowy ekran
>>> u = KontoUzytkownika() >>> u.password = "tajne" Błąd: Hasło zbyt krótkie! (min 8 znaków) >>> u.password = "bardzotajnehaslo" >>> print(u.password) ********
Wnioski do opracowania
  • Szczegółowo opisz mechanizm Name Mangling i wyjaśnij, pod jaką konkretną nazwą Python faktycznie zapisał zmienną __haslo w pamięci.
  • Wyjaśnij cel celowego maskowania danych wrażliwych w getterze właściwości (np. zwracanie stałego ciągu gwiazdek zamiast jawnego tekstu hasła).
  • Omów znaczenie wdrażania rygorystycznych zasad walidacji złożoności haseł jako kluczowego elementu nowoczesnej strategii "security by design".
  • Przeanalizuj techniczne różnice między stosowaniem pojedynczego (_) a podwójnego (__) podkreślnika w kontekście ochrony prywatności.
  • Zastanów się i uzasadnij, czy mechanizm name mangling można uznać za całkowite i wystarczające zabezpieczenie przed nieautoryzowanym dostępem do danych.
  • Wnioskuj o przydatności polecenia systemowego dir(u) do badania i analizy wewnętrznej struktury obiektów posiadających ukryte pola.
  • Porównaj poziom bezpieczeństwa aplikacji przechowującej hasła użytkowników w sposób jawny do aplikacji rygorystycznie stosującej zasady hermetyzacji.
  • Opisz najlepsze praktyki informowania użytkownika o niespełnieniu wymogów bezpieczeństwa (np. zbyt krótkie hasło) bezpośrednio wewnątrz settera.
  • Sprawdź eksperymentalnie, czy próba bezpośredniego odczytu atrybutu __haslo z zewnątrz klasy rzuca spodziewany wyjątek AttributeError.

Rozwiązanie