Programare orientată pe obiecte
Clase și obiecte
Python are multe tipuri încorporate, cum ar fi int, str și altele, pe care le putem folosi în program. Dar Python permite și definirea de tipuri proprii cu ajutorul claselor. O clasă reprezintă o anumită entitate. Implementarea concretă a unei clase este un obiect.
Se poate face o analogie. Fiecare dintre noi are o anumită idee despre un om: acesta are un nume, o vârstă și alte caracteristici. Omul poate realiza anumite acțiuni - merge, aleargă, gândește etc. Această idee, care include un set de caracteristici și acțiuni, poate fi numită clasă.
Implementarea concretă a acestui șablon poate varia, de exemplu, unii oameni au un nume, alții au alt nume. Un om existent în realitate va reprezenta un obiect al acestei clase.
În Python, o clasă se definește cu ajutorul cuvântului cheie class:
class NumeClasa:
atribute_clasa
metode_clasa
În interiorul clasei se definesc atributele sale, care stochează diverse caracteristici ale clasei, și metodele - funcțiile clasei.
Să creăm o clasă simplă:
class Person:
pass
În acest caz, am definit clasa Person, care condiționat reprezintă un om. În acest exemplu, clasa nu conține niciun fel de metode sau atribute. Totuși, pentru că trebuie să fie definit ceva în interiorul clasei, se folosește operatorul pass. Acest operator se utilizează când este necesar să definim sintactic un cod, dar nu avem nevoie de acel cod în contextul respectiv, și în loc de cod concret, introducem operatorul pass.
După crearea clasei, putem defini obiecte ale acestei clase. De exemplu:
class Person:
pass
tom = Person() # definirea obiectului tom
bob = Person() # definirea obiectului bob
După ce am definit clasa Person, am creat două obiecte ale clasei Person - tom și bob. Pentru a crea un obiect, se folosește o funcție specială - constructorul, care se numește după numele clasei și returnează un obiect al clasei. În acest caz, apelul Person() reprezintă apelul constructorului. Fiecare clasă are implicit un constructor fără parametri:
tom = Person() # Person() - apelul constructorului care returnează un obiect al clasei Person
Constructori
Pentru a crea un obiect al clasei, se utilizează un constructor. În exemplul de mai sus, când am creat obiecte ale clasei Person, am folosit constructorul implicit care nu primește parametri și pe care toate clasele îl au implicit. Dar putem defini explicit constructori în clase cu ajutorul metodei speciale __init__() (cu două underscore-uri pe fiecare parte). De exemplu, să modificăm clasa Person, adăugând un constructor:
class Person:
# constructorul
def __init__(self):
print("Crearea obiectului Person")
tom = Person() # Crearea obiectului Person
În codul de mai sus, în clasa Person este definit un constructor - funcția __init__. Constructorul trebuie să primească cel puțin un parametru, referința la obiectul curent - self. Constructorii sunt folosiți de obicei pentru a defini acțiuni care trebuie realizate la crearea obiectului.
Acum, la crearea obiectului:
tom = Person()
Se va apela constructorul __init__() din clasa Person, care va afișa pe consolă mesajul "Crearea obiectului Person".
Este important de menționat că constructorul reprezintă de fapt o funcție obișnuită, doar că pentru a apela constructorul se folosește numele clasei. În plus, la apelarea constructorului nu se transmite explicit nici o valoare pentru parametrul self. Python va determina dinamic self în timpul execuției programului.
Atributele obiectului
Atributele stochează starea obiectului. Pentru a defini și seta atribute în interiorul clasei, se poate folosi cuvântul self. De exemplu, să definim următoarea clasă Person:
class Person:
def __init__(self, name, age):
self.name = name # numele persoanei
self.age = age # vârsta persoanei
tom = Person("Tom", 22)
# accesarea atributelor
# obținerea valorilor
print(tom.name) # Tom
print(tom.age) # 22
# schimbarea valorii
tom.age = 37
print(tom.age) # 37
Acum constructorul clasei Person primește doi parametri - name și age. Prin acești parametri se vor transmite numele și vârsta persoanei create. În interiorul constructorului se setează două atribute - name și age:
def __init__(self, name, age):
self.name = name
self.age = age
Atributului self.name i se atribuie valoarea variabilei name. Atributul age primește valoarea parametrului age. Numele atributelor nu trebuie neapărat să coincidă cu numele parametrilor.
Dacă am definit în clasă constructorul __init__ cu parametri (în afară de self), atunci la apelarea constructorului trebuie să transmitem valori pentru acești parametri:
tom = Person("Tom", 22)
În acest caz, parametrului name i se transmite șirul "Tom", iar parametrului age - numărul 22. Apoi, folosind numele obiectului, putem accesa atributele obiectului - obține și modifica valorile lor:
print(tom.name) # obținerea valorii atributului name
tom.age = 37 # schimbarea valorii atributului age
Astfel, putem crea diverse obiecte ale clasei Person cu valori diferite pentru atribute:
class Person:
def __init__(self, name, age):
self.name = name # numele persoanei
self.age = age # vârsta persoanei
tom = Person("Tom", 22)
bob = Person("Bob", 43)
print(tom.name) # Tom
print(bob.name) # Bob
Aici sunt create două obiecte ale clasei Person: tom și bob. Ele corespund definiției clasei Person, având același set de atribute, dar starea lor va fi diferită. Python va determina dinamic obiectul self în fiecare caz. De exemplu:
tom = Person("Tom", 22)
Acesta va fi obiectul tom. Și la apelul:
bob = Person("Bob", 43)
Acesta va fi obiectul bob. În principiu, nu este necesar să definim atribute în interiorul clasei - Python permite să facem asta dinamic, în afara codului:
class Person:
def __init__(self, name, age):
self.name = name # numele persoanei
self.age = age # vârsta persoanei
tom = Person("Tom", 22)
tom.company = "Microsoft"
print(tom.company) # Microsoft
Aici se setează dinamic atributul company, care stochează locul de muncă al persoanei. După setare, putem obține valoarea acestuia. Totuși, acest mod de definire este riscant. De exemplu, dacă încercăm să accesăm atributul înainte de a-l seta, programul va genera o eroare:
tom = Person("Tom", 22)
print(tom.company) # ! Eroare - AttributeError: Person object has no attribute company
Metodele claselor
Metodele clasei reprezintă funcții definite în interiorul clasei, care definesc comportamentul acesteia. De exemplu, să definim clasa Person cu o metodă:
class Person: # definirea clasei Person
def say_hello(self):
print("Hello")
tom = Person()
tom.say_hello() # Hello
Aici este definită metoda say_hello(), care condiționat realizează o salutare - afișează un mesaj pe consolă. La definirea metodelor oricărei clase, la fel ca și în cazul constructorului, primul parametru al metodei reprezintă referința la obiectul curent, numită conform convențiilor self.
Prin această referință, în interiorul clasei putem accesa funcționalitatea obiectului cu care lucrăm. Cu ajutorul numelui obiectului putem accesa metodele acestuia, scrim numele, după care punct și deja numele metodei:
obiect.metoda([parametrii metodei])
Spre exemplu, să apelăm metoda say_hello() să afișăm pe consolă:
tom.say_hello() #Hello
Dacă vrem să transmitem careva parametri în metodă, în clasă după parametrul self, scrim parametrii de care avem nevoie:
class Person: # definirea clasei Person
def say(self, message): # metoda
print(message)
tom = Person()
tom.say("Hello www.fabricadecoduri.com")# Hello www.fabricadecoduri.com
Aici în metoda say() se transmite parametrul message, care îl vom transmite când vom apela metoda dată.
În interiorul clasei dacă vrem să ne adresăm la câmpuri sau la metode, folosim sintaxa self.câmp și respectiv self.metoda:
self.câmp # adresare la câmp
self.metodă # adresare la metodă
De exemplu ca în clasa următoare:
class Person:
def __init__(self, name, age):
self.name = name # numele persoanei
self.age = age # vârsta persoanei
def display_info(self):
print(f"Name: {self.name} Age: {self.age}")
tom = Person("Tom", 22)
tom.display_info() # Name: Tom Age: 22
bob = Person("Bob", 43)
bob.display_info() # Name: Bob Age: 43
Aici este definită metoda display_info(), care afișează informația despre persoană, apelând atributele self.name și self.age
Output-ul programului va fi:
Name: Tom Age: 22
Name: Bob Age: 43
Destructoare
În afară de constructori, clasele în Python pot defini și metode speciale numite destructoare, care sunt apelate atunci când un obiect este șters. Un destructor este reprezentat de metoda __del__(self), similar cu constructorul, unde este transmisă o referință către obiectul curent.
Destructoarele definesc acțiuni care trebuie efectuate când un obiect este șters, cum ar fi eliberarea sau ștergerea resurselor utilizate de obiect.
Destructorul este apelat automat de interpretor; nu este necesar să fie apelat explicit. Iată un exemplu simplu:
class Persoana:
def __init__(self, nume):
self.nume = nume
print("A fost creată o persoană cu numele", self.nume)
def __del__(self):
print("A fost ștearsă o persoană cu numele", self.nume)
tom = Persoana("Tom")
În acest exemplu, destructorul afișează un mesaj când obiectul Persoana este șters. Programul creează un singur obiect Persoana și îl atribuie variabilei tom. Când obiectul este creat, constructorul său (__init__) este apelat automat. Când programul se încheie sau când tom iese din domeniul său de valabilitate (în acest caz, la sfârșitul execuției programului), destructorul (__del__) este apelat automat. Ieșirea programului va fi:
A fost creată o persoană cu numele Tom
A fost ștearsă o persoană cu numele Tom
Alt exemplu:
class Persoana:
def __init__(self, nume):
self.nume = nume
print("A fost creată o persoană cu numele", self.nume)
def __del__(self):
print("A fost ștearsă o persoană cu numele", self.nume)
def creare_persoana():
tom = Persoana("Tom")
creare_persoana()
print("Sfârșitul programului")
În acest alt exemplu, obiectul Persoana este creat și utilizat în interiorul funcției creare_persoana, astfel că viața obiectului Persoana creat este limitată la domeniul funcției respective. Prin urmare, când funcția își încheie execuția, destructorul obiectului Persoana este apelat automat. Ieșirea programului va fi:
A fost creată o persoană cu numele Tom
A fost ștearsă o persoană cu numele Tom
Sfârșitul programului