Sistem de Gestionare Bibliotecă - Tutorial Complet
În C++, folosim struct pentru a grupa date și funcții. În Python, folosim class pentru același scop, dar cu o sintaxă mai simplă și mai flexibilă.
struct Student {
int id;
char first_name[100];
char last_name[100];
int age;
void afisare() {
cout << "Id:" << id << endl;
cout << "First name:" << first_name << endl;
}
};class Student:
def __init__(self, id, first_name, last_name, age):
self.id = id
self.first_name = first_name
self.last_name = last_name
self.age = age
def afisare(self):
print(f"Id: {self.id}")
print(f"First name: {self.first_name}")__init__ înlocuiește inițializarea manuală din C++. Variabilele membre sunt create dinamic cu self.nume_variabila.Apasă butoanele pentru a simula câteva acțiuni ale unei biblioteci. (E doar o simulare în JavaScript pentru a vizualiza rezultatele rapid în pagină.)
| Caracteristică | C++ | Python |
|---|---|---|
| Declarare structură/clasă | struct NumeStruct { }; | class NumeClasa: |
| Constructor | Constructor explicit sau inițializare manuală | def __init__(self, params): |
| Membri | Declarați explicit cu tip | Creați dinamic în __init__ |
| Arrays | char nume[100] | String-uri sau liste: self.nume = "" |
| Funcții membre | void functie() { } | def functie(self): |
| Acces membri | obiect.membru | obiect.membru |
| String operations | strcpy(dest, src) | dest = src |
| Input | cin.getline(buffer, size) | input("mesaj") |
| Conversie string-int | atoi(string) | int(string) |
void adaugaCarte(Carte carte) {
carti[nr_carti] = carte;
nr_carti++;
}def adauga_carte(self, carte):
self.carti.append(carte)
# Nr de cărți se calculează automat cu len(self.carti)Creați o clasă Student în Python cu următoarele cerințe:
class Student:
def __init__(self, id, nume, prenume, email, varsta):
self.id = id
self.nume = nume
self.prenume = prenume
self.email = email
self.varsta = varsta
def afisare(self):
print(f"ID: {self.id}")
print(f"Nume complet: {self.nume} {self.prenume}")
print(f"Email: {self.email}")
print(f"Vârstă: {self.varsta} ani")
def actualizeaza_email(self, email_nou):
self.email = email_nou
print(f"Email actualizat cu succes la: {self.email}")
def este_major(self):
return self.varsta >= 18
# Testare
student1 = Student(1, "Popescu", "Ion", "ion@email.com", 20)
student1.afisare()
print(f"Este major? {student1.este_major()}")Implementați clasa Carte cu:
from datetime import datetime, timedelta
class Carte:
def __init__(self, id, id_student, nume_carte, data_imprumut=None):
self.id = id
self.id_student = id_student
self.nume_carte = nume_carte
self.data_imprumut = data_imprumut or datetime.now()
def calculeaza_zile_imprumut(self):
if isinstance(self.data_imprumut, str):
data = datetime.strptime(self.data_imprumut, "%Y-%m-%d")
else:
data = self.data_imprumut
return (datetime.now() - data).days
def este_intarziata(self):
return self.calculeaza_zile_imprumut() > 14
def __str__(self):
status = "⚠️ ÎNTÂRZIATĂ" if self.este_intarziata() else "✅ În termen"
return f"[{self.id}] {self.nume_carte} - Student #{self.id_student} - {status}"
def afisare(self):
print(f"ID Carte: {self.id}")
print(f"ID Student: {self.id_student}")
print(f"Nume carte: {self.nume_carte}")
print(f"Data împrumut: {self.data_imprumut}")
print(f"Zile împrumut: {self.calculeaza_zile_imprumut()}")
# Testare
carte1 = Carte(1, 101, "Python pentru începători", "2024-10-01")
print(carte1)
carte1.afisare()Creați clasa Biblioteca care să gestioneze cărțile:
from datetime import datetime
class Biblioteca:
def __init__(self):
self.carti = []
self._index_dupa_id = {}
self._index_dupa_student = {}
def adauga_carte(self, carte):
if carte.id in self._index_dupa_id:
raise ValueError(f"Există deja o carte cu id-ul {carte.id}")
self.carti.append(carte)
self._index_dupa_id[carte.id] = carte
self._index_dupa_student.setdefault(carte.id_student, []).append(carte)
def gaseste_dupa_id(self, id_carte):
return self._index_dupa_id.get(id_carte)
def carti_pentru_student(self, id_student):
return list(self._index_dupa_student.get(id_student, []))
def sterge_carte(self, id_carte):
carte = self._index_dupa_id.pop(id_carte, None)
if not carte:
return False
# Scoatem din lista principală
self.carti = [c for c in self.carti if c.id != id_carte]
# Scoatem din indexul pe student
lista = self._index_dupa_student.get(carte.id_student, [])
self._index_dupa_student[carte.id_student] = [c for c in lista if c.id != id_carte]
if not self._index_dupa_student[carte.id_student]:
self._index_dupa_student.pop(carte.id_student)
return True
def statistici(self):
total = len(self.carti)
intarziate = [c for c in self.carti if hasattr(c, 'este_intarziata') and c.este_intarziata()]
pe_student = {sid: len(lst) for sid, lst in self._index_dupa_student.items()}
return {
'total_carti': total,
'carti_intarziate': len(intarziate),
'distributie_pe_student': pe_student
}
# Testare rapidă
if __name__ == '__main__':
from datetime import timedelta
b = Biblioteca()
b.adauga_carte(Carte(1, 101, 'Clean Code', datetime.now()))
b.adauga_carte(Carte(2, 101, 'Design Patterns', datetime.now()))
b.adauga_carte(Carte(3, 102, 'Fluent Python', datetime.now() - timedelta(days=20)))
print('Toate cărțile studentului 101:')
for c in b.carti_pentru_student(101):
print(' -', c)
print('Statistici:', b.statistici())
b.sterge_carte(2)
print('După ștergere, statistici:', b.statistici())Varianta de mai jos combină clasele Student, Carte și Biblioteca într-un mic script demonstrativ.
from dataclasses import dataclass
from datetime import datetime, timedelta
from typing import List, Dict
@dataclass
class Student:
id: int
nume: str
prenume: str
email: str
varsta: int
def este_major(self) -> bool:
return self.varsta >= 18
class Carte:
def __init__(self, id: int, id_student: int, nume_carte: str, data_imprumut=None):
self.id = id
self.id_student = id_student
self.nume_carte = nume_carte
self.data_imprumut = data_imprumut or datetime.now()
def calculeaza_zile_imprumut(self) -> int:
if isinstance(self.data_imprumut, str):
data = datetime.strptime(self.data_imprumut, "%Y-%m-%d")
else:
data = self.data_imprumut
return (datetime.now() - data).days
def este_intarziata(self) -> bool:
return self.calculeaza_zile_imprumut() > 14
def __str__(self) -> str:
status = "⚠️ ÎNTÂRZIATĂ" if self.este_intarziata() else "✅ În termen"
return f"[{self.id}] {self.nume_carte} - Student #{self.id_student} - {status}"
class Biblioteca:
def __init__(self):
self.carti: List[Carte] = []
self._index_dupa_id: Dict[int, Carte] = {}
self._index_dupa_student: Dict[int, List[Carte]] = {}
def adauga_carte(self, carte: Carte) -> None:
if carte.id in self._index_dupa_id:
raise ValueError(f"ID duplicat: {carte.id}")
self.carti.append(carte)
self._index_dupa_id[carte.id] = carte
self._index_dupa_student.setdefault(carte.id_student, []).append(carte)
def gaseste_dupa_id(self, id_carte: int) -> Carte | None:
return self._index_dupa_id.get(id_carte)
def carti_pentru_student(self, id_student: int) -> List[Carte]:
return list(self._index_dupa_student.get(id_student, []))
def sterge_carte(self, id_carte: int) -> bool:
carte = self._index_dupa_id.pop(id_carte, None)
if not carte:
return False
self.carti = [c for c in self.carti if c.id != id_carte]
lst = self._index_dupa_student.get(carte.id_student, [])
self._index_dupa_student[carte.id_student] = [c for c in lst if c.id != id_carte]
if not self._index_dupa_student[carte.id_student]:
self._index_dupa_student.pop(carte.id_student)
return True
def statistici(self) -> dict:
intarziate = [c for c in self.carti if c.este_intarziata()]
return {
'total_carti': len(self.carti),
'carti_intarziate': len(intarziate),
'distributie_pe_student': {sid: len(lst) for sid, lst in self._index_dupa_student.items()}
}
if __name__ == '__main__':
# Date de test
s1 = Student(101, 'Popescu', 'Ana', 'ana@ex.com', 21)
s2 = Student(102, 'Ionescu', 'Mihai', 'mihai@ex.com', 17)
b = Biblioteca()
b.adauga_carte(Carte(1, s1.id, 'Clean Code', datetime.now() - timedelta(days=10)))
b.adauga_carte(Carte(2, s1.id, 'Design Patterns', datetime.now() - timedelta(days=30)))
b.adauga_carte(Carte(3, s2.id, 'Fluent Python', datetime.now()))
print('— Toate cărțile —')
for c in b.carti:
print(' ', c)
print('\n— Cărți pentru studentul 101 —')
for c in b.carti_pentru_student(101):
print(' ', c)
print('\n— Statistici —')
print(b.statistici())@dataclass + validărifrom dataclasses import dataclass, field
from datetime import datetime
from typing import ClassVar
@dataclass
class Carte:
MAX_ZILE: ClassVar[int] = 14
id: int
id_student: int
nume_carte: str
data_imprumut: datetime = field(default_factory=datetime.now)
def __post_init__(self):
if not self.nume_carte:
raise ValueError("nume_carte nu poate fi gol")
@property
def zile(self) -> int:
return (datetime.now() - self.data_imprumut).days
@property
def intarziata(self) -> bool:
return self.zile > self.MAX_ZILEimport json
from pathlib import Path
from datetime import datetime
class BibliotecaPersistenta(Biblioteca):
def salveaza(self, fisier: str):
payload = [
{
'id': c.id,
'id_student': c.id_student,
'nume_carte': c.nume_carte,
'data_imprumut': c.data_imprumut.strftime('%Y-%m-%d')
} for c in self.carti
]
Path(fisier).write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding='utf-8')
def incarca(self, fisier: str):
self.carti.clear(); self._index_dupa_id.clear(); self._index_dupa_student.clear()
data = json.loads(Path(fisier).read_text(encoding='utf-8'))
for row in data:
c = Carte(
row['id'], row['id_student'], row['nume_carte'],
datetime.strptime(row['data_imprumut'], '%Y-%m-%d')
)
self.adauga_carte(c)from datetime import datetime, timedelta
from app import Biblioteca, Carte
def test_adaugare_si_statistici():
b = Biblioteca()
b.adauga_carte(Carte(1, 101, 'X', datetime.now() - timedelta(days=20)))
b.adauga_carte(Carte(2, 101, 'Y', datetime.now()))
stats = b.statistici()
assert stats['total_carti'] == 2
assert stats['carti_intarziate'] == 1