Skip to content

Clase

Clasele sunt baza programării orientate pe obiecte (OOP). O clasă definește un tip de date personalizat care grupează date (atribute) și funcționalități (metode).

Concepte fundamentale

  • Clasă: un șablon/model pentru crearea obiectelor
  • Obiect/Instanță: o realizare concretă a unei clase
  • Atribute: variabile care aparțin clasei/obiectului
  • Metode: funcții definite în interiorul clasei

Definirea unei clase

class Persoana:
    # Constructor - se apelează la crearea obiectului
    def __init__(self, nume, varsta):
        self.nume = nume      # atribut de instanță
        self.varsta = varsta  # atribut de instanță

    # Metodă
    def salut(self):
        print(f"Salut, sunt {self.nume}!")

    def prezentare(self):
        return f"{self.nume}, {self.varsta} ani"

Crearea obiectelor

# Creare instanțe
persoana1 = Persoana("Ana", 25)
persoana2 = Persoana("Ion", 30)

# Accesare atribute
print(persoana1.nume)    # Ana
print(persoana2.varsta)  # 30

# Apelare metode
persoana1.salut()                  # Salut, sunt Ana!
print(persoana2.prezentare())      # Ion, 30 ani

# Modificare atribute
persoana1.varsta = 26

Parametrul self

self reprezintă referința către instanța curentă a clasei. Este primul parametru al oricărei metode și permite accesul la atributele și metodele obiectului.

class Dreptunghi:
    def __init__(self, lungime, latime):
        self.lungime = lungime
        self.latime = latime

    def aria(self):
        return self.lungime * self.latime

    def perimetru(self):
        return 2 * (self.lungime + self.latime)

d = Dreptunghi(5, 3)
print(d.aria())       # 15
print(d.perimetru())  # 16

Atribute de clasă vs instanță

class Student:
    # Atribut de clasă - partajat de toate instanțele
    scoala = "Liceul Teoretic"
    numar_studenti = 0

    def __init__(self, nume, clasa):
        # Atribute de instanță - unice pentru fiecare obiect
        self.nume = nume
        self.clasa = clasa
        Student.numar_studenti += 1

s1 = Student("Ana", "10A")
s2 = Student("Ion", "10B")

print(s1.scoala)              # Liceul Teoretic
print(s2.scoala)              # Liceul Teoretic
print(Student.numar_studenti) # 2

Metode speciale (dunder methods)

Metodele cu __nume__ sunt metode speciale care definesc comportamente standard.

__str__ și __repr__

class Punct:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        # Pentru print() și str()
        return f"Punct({self.x}, {self.y})"

    def __repr__(self):
        # Pentru debugging și reprezentare oficială
        return f"Punct(x={self.x}, y={self.y})"

p = Punct(3, 4)
print(p)       # Punct(3, 4)
print(repr(p)) # Punct(x=3, y=4)

Operatori aritmetici

Poți defini cum se comportă obiectele cu operatori (+, -, *, etc.):

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y)

    def __sub__(self, other):
        return Vector(self.x - other.x, self.y - other.y)

    def __mul__(self, scalar):
        return Vector(self.x * scalar, self.y * scalar)

    def __eq__(self, other):
        return self.x == other.x and self.y == other.y

    def __str__(self):
        return f"({self.x}, {self.y})"

v1 = Vector(1, 2)
v2 = Vector(3, 4)

print(v1 + v2)  # (4, 6)
print(v2 - v1)  # (2, 2)
print(v1 * 3)   # (3, 6)
print(v1 == v2) # False

__len__ și __getitem__

Pentru a permite len() și indexarea cu []:

class Colectie:
    def __init__(self, elemente):
        self.elemente = elemente

    def __len__(self):
        return len(self.elemente)

    def __getitem__(self, index):
        return self.elemente[index]

    def __setitem__(self, index, valoare):
        self.elemente[index] = valoare

c = Colectie([1, 2, 3, 4, 5])
print(len(c))   # 5
print(c[0])     # 1
c[0] = 10
print(c[0])     # 10

Încapsulare

Încapsularea înseamnă ascunderea detaliilor interne și expunerea doar a unei interfețe controlate.

Atribute private (convenție)

În Python, prefixul _ indică un atribut "protejat" (convenție, nu e forțat):

class ContBancar:
    def __init__(self, titular, sold_initial):
        self.titular = titular
        self._sold = sold_initial  # convenție: atribut "protejat"

    def depune(self, suma):
        if suma > 0:
            self._sold += suma

    def retrage(self, suma):
        if 0 < suma <= self._sold:
            self._sold -= suma
            return True
        return False

    def get_sold(self):
        return self._sold

cont = ContBancar("Ana", 1000)
cont.depune(500)
print(cont.get_sold())  # 1500

Property (getters/setters)

@property permite accesul la atribute cu validare:

class Cerc:
    def __init__(self, raza):
        self._raza = raza

    @property
    def raza(self):
        return self._raza

    @raza.setter
    def raza(self, valoare):
        if valoare > 0:
            self._raza = valoare
        else:
            raise ValueError("Raza trebuie să fie pozitivă")

    @property
    def aria(self):
        import math
        return math.pi * self._raza ** 2

c = Cerc(5)
print(c.raza)   # 5
print(c.aria)   # 78.54...
c.raza = 10
print(c.aria)   # 314.15...

Moștenire

O clasă poate moșteni atribute și metode de la o altă clasă (clasa părinte/de bază).

# Clasa părinte (de bază)
class Animal:
    def __init__(self, nume):
        self.nume = nume

    def vorbeste(self):
        pass

# Clase copil (derivate)
class Caine(Animal):
    def vorbeste(self):
        return f"{self.nume} zice: Ham!"

class Pisica(Animal):
    def vorbeste(self):
        return f"{self.nume} zice: Miau!"

caine = Caine("Rex")
pisica = Pisica("Whiskers")

print(caine.vorbeste())   # Rex zice: Ham!
print(pisica.vorbeste())  # Whiskers zice: Miau!

Extinderea constructorului

Folosești super() pentru a apela constructorul clasei părinte:

class Angajat:
    def __init__(self, nume, salariu):
        self.nume = nume
        self.salariu = salariu

class Manager(Angajat):
    def __init__(self, nume, salariu, departament):
        super().__init__(nume, salariu)  # apelează constructorul părintelui
        self.departament = departament

    def prezentare(self):
        return f"{self.nume}, Manager {self.departament}"

m = Manager("Ion", 5000, "IT")
print(m.prezentare())  # Ion, Manager IT
print(m.salariu)       # 5000

Verificare tip

print(isinstance(caine, Caine))   # True
print(isinstance(caine, Animal))  # True
print(isinstance(caine, Pisica))  # False

print(issubclass(Caine, Animal))  # True
print(issubclass(Caine, Pisica))  # False

Polimorfism

Polimorfismul permite ca același nume de metodă să aibă comportament diferit în clase diferite:

class Forma:
    def aria(self):
        pass

class Dreptunghi(Forma):
    def __init__(self, lungime, latime):
        self.lungime = lungime
        self.latime = latime

    def aria(self):
        return self.lungime * self.latime

class Cerc(Forma):
    def __init__(self, raza):
        self.raza = raza

    def aria(self):
        import math
        return math.pi * self.raza ** 2

# Funcție care lucrează cu orice formă
def afiseaza_aria(forma):
    print(f"Aria: {forma.aria():.2f}")

forme = [Dreptunghi(5, 3), Cerc(4)]
for forma in forme:
    afiseaza_aria(forma)
# Aria: 15.00
# Aria: 50.27

Metode de clasă și statice

class Utilitar:
    valoare_clasa = 0

    def __init__(self, valoare):
        self.valoare = valoare

    # Metodă de instanță - acces la self
    def metoda_instanta(self):
        return f"Valoare instanță: {self.valoare}"

    # Metodă de clasă - acces la cls (clasa însăși)
    @classmethod
    def metoda_clasa(cls):
        return f"Valoare clasă: {cls.valoare_clasa}"

    @classmethod
    def creeaza_cu_valoare_dubla(cls, valoare):
        return cls(valoare * 2)

    # Metodă statică - fără acces la self sau cls
    @staticmethod
    def metoda_statica(x, y):
        return x + y

u = Utilitar(10)
print(u.metoda_instanta())           # Valoare instanță: 10
print(Utilitar.metoda_clasa())       # Valoare clasă: 0
print(Utilitar.metoda_statica(3, 4)) # 7

u2 = Utilitar.creeaza_cu_valoare_dubla(5)
print(u2.valoare)  # 10

Aplicații practice

Sistem de gestiune bibliotecă

class Carte:
    def __init__(self, titlu, autor, isbn):
        self.titlu = titlu
        self.autor = autor
        self.isbn = isbn
        self.disponibila = True

    def __str__(self):
        status = "disponibilă" if self.disponibila else "împrumutată"
        return f"{self.titlu} de {self.autor} ({status})"

class Biblioteca:
    def __init__(self):
        self.carti = []

    def adauga_carte(self, carte):
        self.carti.append(carte)

    def cauta_dupa_autor(self, autor):
        return [c for c in self.carti if autor.lower() in c.autor.lower()]

    def imprumuta(self, isbn):
        for carte in self.carti:
            if carte.isbn == isbn and carte.disponibila:
                carte.disponibila = False
                return True
        return False

    def returneaza(self, isbn):
        for carte in self.carti:
            if carte.isbn == isbn:
                carte.disponibila = True
                return True
        return False

# Utilizare
biblioteca = Biblioteca()
biblioteca.adauga_carte(Carte("Ion", "Liviu Rebreanu", "123"))
biblioteca.adauga_carte(Carte("Mara", "Ioan Slavici", "456"))

for carte in biblioteca.carti:
    print(carte)

Joc simplu - Personaje

class Personaj:
    def __init__(self, nume, viata, atac):
        self.nume = nume
        self.viata = viata
        self.atac = atac

    def este_viu(self):
        return self.viata > 0

    def ataca(self, inamic):
        if self.este_viu() and inamic.este_viu():
            inamic.viata -= self.atac
            print(f"{self.nume} atacă pe {inamic.nume} cu {self.atac} damage!")

class Erou(Personaj):
    def __init__(self, nume, viata, atac, mana):
        super().__init__(nume, viata, atac)
        self.mana = mana

    def vindeca(self, cantitate=20):
        if self.mana >= 10:
            self.viata += cantitate
            self.mana -= 10
            print(f"{self.nume} s-a vindecat cu {cantitate} HP!")

class Monstru(Personaj):
    def atac_special(self, inamic):
        damage = self.atac * 2
        inamic.viata -= damage
        print(f"{self.nume} folosește atac special: {damage} damage!")

# Simulare luptă
erou = Erou("Arthur", 100, 15, 50)
monstru = Monstru("Dragon", 80, 10)

erou.ataca(monstru)
monstru.atac_special(erou)
erou.vindeca()