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?
- Potrzebuje oprogramowania do przechowywania informacji na temat poznanych ludzi. Imię, nazwisko itd
- Prawdopodobnie, z czasem pojawią się ludzie odgrywający różne role: żołnierz, VIP itd
- Krytyczne jest zrozumienie i notowanie sieci powiązań. Czyli kto, kogo zna.
- Informacje są ściśle tajne. Nikt nie może mieć do nich dostępu bez zalogowania, tajnym hasłem i loginem
- 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
- Wstęp do kursu
- Moduły i pakiety
- Programowanie obiektowe – wstęp
- Programowanie obiektowe – dziedziczenie
- Programowanie obiektowe – hermetyzacja
- Mini projekt – Organizer
- Funkcje rekurencyjne w Python
- Pisanie skryptów w Python
- Mini projekt – tajny agent, generator haseł
- *args oraz **kwargs
- Dekoratory w Python
- Mini projekt – tajny agent – kontakty <– bieżący projekt
- Odwzorowanie list
- Podsumowanie oraz dalsze kroki