Nasz tajny agent, na terytorium wrogiego państwa, zaczyna rozpracowywać służby specjalne, odpowiedzialne za planowanie inwazji. Każdego dnia, zdobywa zaufanie, dostęp do baz danych, a jego wiedza na temat ludzi, rośnie. Nadszedł czas, aby się zorganizować – notować oraz analizować sieć kontaktów. Niestety nie ma, do tego celu dedykowanego oprogramowania. Na szczęście jest po kursie Python na Analityk.edu.pl i doskonale wie jak szybko i sprawnie, napisać potrzebny program.

Co nasz tajny agent wie?

  1. Potrzebuje oprogramowania do przechowywania informacji na temat poznanych ludzi. Imię, nazwisko itd
  2. Prawdopodobnie, z czasem pojawią się ludzie odgrywający różne role: żołnierz, VIP itd
  3. Krytyczne jest zrozumienie i notowanie sieci powiązań. Czyli kto, kogo zna.
  4. Informacje są ściśle tajne. Nikt nie może mieć do nich dostępu bez zalogowania, tajnym hasłem i loginem
  5. Jest świeżo, po lekcjach na analityk.edu.pl, tak więc z przyjemnością przetestuje nowo nabytą wiedzę.

W pierwszej kolejności, spróbujmy przewidzieć obiekty, jakie będziemy potrzebować.

kontakty – pierwsza klasa, która przychodzi na myśl. Aczkolwiek, w zależności od rodzaju zajmowanej pozycji, możemy mieć potrzebę przechowywania różnych informacji. Możemy sobie wyobrazić, że inne informacje będziemy chcieć przechowywać na temat żołnierza, a inne na temat osoby VIP czy też osoby prywatnej. Możemy pokusić się o zdefiniowanie osobnych klas, dla żołnierza oraz innych osób, natomiast wszystkie mogą dziedziczyć od jednej klasy – kontakt, tak aby wymusić cechy i zachowania wspólne.

from abc import ABC, abstractmethod 

class kontact(ABC):
    
    def __init__(self, type, numer, imie, nazwisko):
        pass
    
    @abstractmethod
    def __str__(self):
        pass
    
class zolnierz(kontact):
    
    def __init__(self, numer, imie, nazwisko):
        pass
    
    def __str__(self):
        pass

Klasa abstrakcyjna ,’kontakt’, narzuca obowiązek, inicjalizacji obiektu, z co najmniej 4 parametrami:

  • ‚type’ – odpowiada za informację, czy jest to żołnierz, VIP czy inna rola,
  • ‚numer’ – jest to unikalny numer identyfikacyjny, danej osoby, w bazie danych
  • ‚imie’ oraz ‚nazwisko’

Pamiętamy również, że kluczową informacją, jaką chcemy przechowywać, jest sieć znajomości. Możemy wykorzystać do tego celu, listy, w której będziemy dla każdej osoby, przechowywać informację o numerach osób które zna. Numer, który jest unikalny, będzie lepszym rozwiązaniem niż imię oraz nazwisko.

Tym samym, nasz konstruktor, może utworzyć kolejną zmienną ‚znajomi’.

Dodatkowo posiada, metodę abstrakcyjną ‚__str__’, wymusza obowiązek obsługi funkcji print.

Klasa ‚zolnierz’, również posiada konstruktor, który w pierwszej kolejności będzie musiał, wywołać konstruktor, klasy ‚kontakt’.

Następnie, musimy nadpisać metodę ‚__str__’.

W rezultacie, kody naszych obiektów, mogą wyglądać następująco:

from abc import ABC, abstractmethod 

class kontact(ABC):
    
    def __init__(self, type, numer, imie, nazwisko):
        self.type = type
        self.numer = numer
        self.imie = imie
        self.nazwisko = nazwisko
        self.znajomi = []
    
    @abstractmethod
    def __str__(self):
        pass
    
class zolnierz(kontact):
    
    def __init__(self, numer, imie, nazwisko):
        super().__init__('żołnierz', numer, imie, nazwisko)
    
    def __str__(self):
        info = str(self.numer) + " " + self.imie + " " + self.nazwisko + "\n"
        info += "Zna:" + str(self.znajomi)
        return info

network – w drugiej kolejności, zajmiemy się klasą odpowiedzialną za całą sieć kontaktów. Będzie ona zawierać bazę danych osób, oraz metody, pozwalające na niej operować. Wyświetlać kontakty, dodawać kontakty, dodawać informacje, kto, kogo zna oraz autoryzować użytkownika.

class network():
    
    __osoby = []
    __autoryzowany = False
    __tajnyLogin = 'analityk'
    __tajneHaslo = 'edu.pl'
    
    def autoryzuj(self):
        pass    

    def wyswietl(self, lista, *args):
        pass
    
    def dodajOsobe(self, numer):
        pass
            
    def dodajZnajomego(self, osoby):
        pass
    
    def wykonuj(self):    
        pass

Zmienne:

  • __osoby – będziemy w niej przechowywać listę osób w naszej sieci, czyli obiekty typu ‚zolnierze’, ‚VIP’ itd
  • __autoryzowany – inforacja czy użytkownik się poprawnie autoryzował
  • __tajnyLogin i __tajneHasło – jest to login i hasło, które powinien podać użytkownik. W praktyce nigdy nie powinniśmy w ten sposób przechowywać takich informacji, jednak na potrzeby naszego zadania, możemy

Metody:

  • autoryzuj – będzie odpytywać użytkownika o login i hasło, po czym sprawdzać z __tajnymLoginem i __tajnymHaslem, czy się zgadzają
  • wyświetl – wyświetla liste osób oraz ich sieć kontaktów
  • dodajOsobę – dodaje osobę do bazy danych osób w __osoby
  • dodajZnajomego – dodaje informację o znajomości
  • wykonuj – wyświetla menu, prosząc użytkownika o wybór działania, które chce wykonać.

Metoda autoryzuj:

Metoda jest bardzo prosta. Pobiera od użytkownika hasło i login, sprawdza z danymi __tajne, po czym zwraca True, jeżeli się zgadzają i False, jeżeli nie.

    def autoryzuj(self):
        login = input('Podaj login:')
        haslo = input('Podaj haslo:')
        if login == self.__tajnyLogin and haslo == self.__tajneHaslo:
            print("Autoryzacja poprawna")
            return True
        else:
            print("Hasło lub login niepoprawny")
            return False

Metoda wyświetl:

Możemy użyć pętli for, aby przejść przez listę osób, po czym ją wydrukować.

    def wyswietl(self, lista):
        for i in lista:
            print(i)

O wiele prościej, jednak warto spróbować obydwu sposobów, aby nabrać wprawy.

Metoda dodajOsobe:

Metoda, ma za zadanie wczytać dane od użytkownika, na temat osoby, którą chcemy dodać do naszej bazy danych, a następnie utworzyć i zwrócić nowy obiekt.

    def dodajOsobe(self, numer):
        imie = input("Imię:")
        nazwisko = input("Nazwisko:")
        new = zolnierz(numer, imie, nazwisko)
        return new

Metoda dodajZnajomego:

Metoda, powinna wczytywać informację od użytkownika, kto, zna kogo. Wystarczy że użytkownik, poda unikalny numer danej osoby. Następnie, metoda ma 2 zadania. Musi znaleźć osobę o pierwszym numerze, i dodać drugą osobę, do listy znajomych, po czym, wykonać to samo, ale dla drugiej osoby. Tak aby listy znajomych obydwu osób, zostały zmodyfikowane.

Podobnie jak w przypadku metody wyświetl, możemy ją wykonać za pomocą pętli for, lub funkcji rekurencyjnych.

Poniżej, pokażemy drugie podejście, natomiast pierwsze, zostawimy do wykonania, czytelnikowi.

    def dodajZnajomego(self, osoby):
        a = int(input("Numer osoby 1"))
        b = int(input("Numer osoby 2"))
        
        for o in osoby:
            if o.numer == a:
                o.znajomi = o.znajomi + [b]
            if o.numer == b:
                o.znajomi = o.znajomi + [a]
        
        return osoby

Metoda wykonuj:

Metoda wykonuj, ma za zadanie wczytać polecenie od użytkownika i wykonać odpowiednią akcję:

    def wykonuj(self):    
        while True:
            print("""
                1 - wyświetl sieć kontaktów
                2 - dodaj osobę
                3 - dodaj powiązanie pomiędzy kontaktami
                4 - zaloguj
                9 - zamknij \n
            """)
            x = input("co chcesz zrobić?")
            
            if x == '1': 
                self.wyswietl(self.__osoby)
            if x == '2':
                nowa = self.dodajOsobe(len(self.__osoby)+1)
                if nowa != None: self.__osoby.append(nowa)
            if x == '3': 
                self.dodajZnajomego( self.__osoby )
            if x == '4': 
                if self.autoryzuj():
                    self.__autoryzowany = True
                else:
                    self.__autoryzowany = False
            if x == '9': 
                import sys
                sys.exit()

Nie ma tutaj miejsca na komentarz, oprócz możliwej dyskusji, czy funkcja, powinna znajdować się wewnątrz samej klasy notatnik, czy też jej logika powinna być umieszczona po za klasą, tak jak w poprzednim mini projekcie.

O czym zapomnieliśmy?

Pomimo, że posiadamy funkcję pozwalającą się nam autoryzować, możemy wykonywać różne funkcje, takie jak ‚wyświetl’, bez autoryzacji.

Jak się zabezpieczyć, przed wykonaniem funkcji bez autoryzacji?

Bardzo prosto. Wystarczy, że napiszemy dekorator, sprawdzający czy użytkownik jest zalogowany, następnie, udekorujemy nim funkcję, które nie chcemy aby mogły być wykonane, bez poprawnej autoryzacji.

    def zalogowany(funk):
        
        def wew(self, *args, **kwargs):
            
            if self.__autoryzowany == False:
                print("Musisz być zalogowany.")
                return
            else:
                return funk(self, *args, **kwargs)
         
        return wew  

I w ten sposób, dochodzimy, do przykładowego rozwiązania naszego problemu, które może wyglądać następująco. Jest to tylko przykładowe, ale nie optymalne.

Kody źródłowe:

from abc import ABC, abstractmethod 

class kontact(ABC):
    
    def __init__(self, type, numer, imie, nazwisko):
        self.type = type
        self.numer = numer
        self.imie = imie
        self.nazwisko = nazwisko
        self.znajomi = []
    
    @abstractmethod
    def __str__(self):
        pass
    
class zolnierz(kontact):
    
    def __init__(self, numer, imie, nazwisko):
        super().__init__('regular', numer, imie, nazwisko)
    
    def __str__(self):
        info = str(self.numer) + " " + self.imie + " " + self.nazwisko + "\n"
        info += "Zna:" + str(self.znajomi)
        return info
    
class network():
    
    __osoby = []
    __autoryzowany = False
    __tajnyLogin = 'analityk'
    __tajneHaslo = 'edu.pl'
    
    def autoryzuj(self):
        login = input('Podaj login:')
        haslo = input('Podaj haslo:')
        if login == self.__tajnyLogin and haslo == self.__tajneHaslo:
            print("Autoryzacja poprawna")
            return True
        else:
            print("Hasło lub login niepoprawny")
            return False
    
    def zalogowany(funk):
        
        def wew(self, *args, **kwargs):
            
            if self.__autoryzowany == False:
                print("Musisz być zalogowany.")
                return
            else:
                return funk(self, *args, **kwargs)
         
        return wew    
    
    @zalogowany
    def wyswietl(self, lista):
        for i in lista:
            print(i)
    
    @zalogowany
    def dodajOsobe(self, numer):
        imie = input("Imię:")
        nazwisko = input("Nazwisko:")
        new = zolnierz(numer, imie, nazwisko)
        return new
            
    @zalogowany
    def dodajZnajomego(self, osoby):
        a = int(input("Numer osoby 1"))
        b = int(input("Numer osoby 2"))
        
        for o in osoby:
            if o.numer == a:
                o.znajomi = o.znajomi + [b]
            if o.numer == b:
                o.znajomi = o.znajomi + [a]
        
        return osoby
    
    
    def wykonuj(self):    
        while True:
            print("""
                1 - wyświetl sieć kontaktów
                2 - dodaj osobę
                3 - dodaj powiązanie pomiędzy kontaktami
                4 - zaloguj
                9 - zamknij \n
            """)
            x = input("co chcesz zrobić?")
            
            if x == '1': 
                self.wyswietl(self.__osoby)
            if x == '2':
                nowa = self.dodajOsobe(len(self.__osoby)+1)
                if nowa != None: self.__osoby.append(nowa)
            if x == '3': 
                self.dodajZnajomego( self.__osoby )
            if x == '4': 
                if self.autoryzuj():
                    self.__autoryzowany = True
                else:
                    self.__autoryzowany = False
            if x == '9': 
                import sys
                sys.exit()
        
        
n = network()
n.wykonuj()

Podsumowując. Nasz problem jest możliwy do rozwiązania, na wiele sposób. powyżej przedstawiony, nie jest optymalny, ale wykorzystuje elementy programowania, które warto przećwiczyć. Zapraszamy do wykonania ćwiczeń, w celu dalszego treningu.

W następnych lekcjach, nauczymy sie obsługwać wyjątki oraz poznamy, nowy, użyteczny i efektywny sposób tworzenia list. Jak widzimy operacje na listach stanowią, ważny element programowania. Python, natomiast, nie narzuca nam jednego sposobu, ale daje kilka możliwości.

Ćwiczenia

Ćwiczenie 1: Należy rozszerzyć nasz dekorator, o logowanie każdej wykonywanej funkcji. Logowanie może odbywać się poprzez drukowanie informacji na ekranie.

Ćwiczenie 2: Należy rozszerzyć nasz program, o możliwość usunięcia wybranej osoby.

Python, NIE tylko dla początkujących

  1. Wstęp do kursu
  2. Moduły i pakiety
  3. Programowanie obiektowe – wstęp
  4. Programowanie obiektowe – dziedziczenie
  5. Programowanie obiektowe – hermetyzacja
  6. Mini projekt – Organizer
  7. Funkcje rekurencyjne w Python
  8. Pisanie skryptów w Python
  9. Mini projekt – tajny agent, generator haseł
  10. *args oraz **kwargs
  11. Dekoratory w Python
  12. Mini projekt – tajny agent – kontakty <– bieżący projekt
  13. Odwzorowanie list
  14. Podsumowanie oraz dalsze kroki