﻿import functools

# --- DEFINICJA WŁASNEGO DEKORATORA ---
# Własny dekorator to funkcja, która przyjmuje inną funkcję (bądź metodę)
# i rozszerza jej działanie bez ingerencji w jej oryginalny kod.
def loguj_wywolanie(func):
    """
    Dekorator logujący wywołanie metody.
    Używamy functools.wraps, aby zachować oryginalną nazwę i docstring dekorowanej funkcji.
    """
    @functools.wraps(func)
    def wrapper(self, *args, **kwargs):
        print(f"[LOG] Wywoływana jest metoda: '{func.__name__}'")
        # Wywołanie oryginalnej funkcji
        return func(self, *args, **kwargs)
    return wrapper


class Student:
    """
    Klasa reprezentująca profil studenta.
    """
    def __init__(self, imie: str, nazwisko: str, indeks: str):
        # self reprezentuje unikalną instancję w pamięci
        self.imie = imie
        self.nazwisko = nazwisko
        self.indeks = indeks
        
    # --- UŻYCIE DEKORATORA @property ---
    # Dekorator @property wbudowany w Pythona zmienia zwykłą metodę w tzw. właściwość (ang. property).
    # Pozwala to na dostęp do wyniku tej metody tak, jakby to był zwykły atrybut (bez nawiasów).
    # Jest to tzw. getter - służy do odczytu wyliczanej wartości.
    @property
    def pelny_profil(self) -> str:
        """
        Właściwość wyliczana dynamicznie, zwracająca sformatowany ciąg znaków.
        """
        return f"Student: {self.imie} {self.nazwisko}, Indeks: {self.indeks}"

    # --- UŻYCIE WŁASNEGO DEKORATORA ---
    # Dekorator uruchomi się tuż przed wywołaniem tej metody, dodając zachowanie z funkcji 'wrapper'.
    @loguj_wywolanie
    def wyswietl_dane(self):
        """
        Metoda prezentująca profil studenta w sformatowanym ciągu znaków.
        Wypisuje dane korzystając z dynamicznej właściwości pelny_profil.
        """
        print(self.pelny_profil)


if __name__ == "__main__":
    print("--- ZADANIE 01 ---")
    # Inicjalizacja wielu, niezależnych instancji
    s1 = Student("Adam", "Nowak", "12345")
    s2 = Student("Ewa", "Kowalska", "67890")

    # Wywołanie metody wyświetlającej - dekorator @loguj_wywolanie wypisze najpierw [LOG]
    s1.wyswietl_dane()
    s2.wyswietl_dane()

    # Dostęp do właściwości obłożonej dekoratorem @property (zauważ brak nawiasów "()")
    print("\nPrzykład użycia @property jako atrybutu:", s1.pelny_profil)
