-
Notifications
You must be signed in to change notification settings - Fork 2
Programare orientată pe obiecte
Programarea orientată pe obiecte permite gruparea datelor și funcțiilor care operează asupra lor într-o singură structură. Noțiunea centrală în programarea orientată pe obiecte este cea de clasă. O clasă este un domeniu în interiorul căruia se definesc o serie de atribute (metode și variabile). Obiectele construite de o clasă poartă numele de instanțe ale clasei.
Se folosește cuvântul rezervat class
, astfel:
class ClassName:
pass # se va înlocui cu corpul clasei
Scrierea numelui clasei CamelCase este o convenție, nu o cerință a limbajului.
Orice clasă este un obiect callable
care construiește o instanță a sa la apelare.
f = Foo()
Astfel, se creează o instanță a clasei Foo
și se păstrează o referință către aceasta în variabila f
.
O metodă este o funcție definită în cadrul unei clase. Primul argument pe care îl primesc metodele este o instanță a clasei de care aparțin; în acest scop se folosește, la definirea metodei, cuvântul rezervat self
.
class Foo:
bar = 0
def setx(self, x):
self.x = x
def printx(self):
print self.x
Apelarea metodelor este similară cu un apel de funcție; transmiterea instanței ca prim parametru se face folosind funcția ca atribut al instanței.
>>> f.setx(2)
>>> f.printx()
2
Membrii clasei de tip dată pot fi:
- variabile ale clasei
- variabile ale instanței
În definiția clasei Foo
de mai sus, bar
este o variabilă a clasei; valoarea acesteia este aceeași pentru toate instanțele clasei și poate fi accesată folosind construcția <nume clasă>.<nume variabilă>
.
>>> Foo.bar
0
>>> Foo.bar += 1
>>> f.bar
1
Un exemplu de variabilă a instanței este x
; acest tip de variabilă este propriu fiecărei instanțe în parte
și se inițializează în cadrul unei metode (aici setx
). Variabilele la nivel de instanță nu pot fi accesate în lipsa unei instanțe a clasei.
>>> Foo.x
AttributeError: class Foo has no attribute 'x'
În Python, membrii unei clase se pot schimba în timpul rulării (nu doar valoarea acestora, ca în C sau Java). Astfel, obiectul f
nu avea atributul x
înainte de apelarea metodei setx
:
>>> f = Foo()
>>> f.x
AttributeError: Foo instance has no attribute 'x'
>>> f.setx(5)
>>> f.x
5
Putem chiar șterge f.x
după rularea codului de mai sus:
>>> del f.x
>>> f.printx()
AttributeError: Foo instance has no attribute 'x'
Datorită structurii dinamice a claselor, se poate schimba definiția unei clase în timpul execuției programului. Mai jos se adaugă clasei Foo
deja definite un atribut y
. Orice nouă instanță a clasei Foo
va avea acest nou atribut.
>>> Foo.y = 10
>>> g = Foo()
>>> g.y
10
Moștenirea permite unei clase să extindă comportamentul uneia sau mai multor clase. Se folosește sintaxa:
class ClassName(superclass1, superclass2, ...):
...
Clasa derivată va moșteni toate atributele clasei părinte. Dacă o metodă este definită atât în clasa părinte, cât și în cea derivată, metoda clasei derivate o suprascrie pe cea a părintelui.
>>> class Foo:
... x = 10
...
... def bar(self):
... print 'I am Foo.bar'
...
>>> class Bar(Foo):
... y = 9
...
... def bar(self):
... print 'I am Bar.bar'
...
>>> b = Bar()
>>> b.bar()
I am Bar.bar
>>> b.x
10
>>> b.y
9
Încapsularea este un conctept cheie în programarea orientată pe obiecte. În Python, pentru a ascunde membrii unei clase, se folosește următoarea convenție:
-
_member
- prefixarea cu un underscore, pentru membri protected, permite modificarea atributului doar în interiorul clasei sau de către un moștenitor; -
__member
- prefixarea cu două underscore-uri, pentru membri private, interzice accesul la atribut din afara clasei.
Exemplu:
class JustCounter:
__secretCount = 0
def count(self):
self.__secretCount += 1
print self.__secretCount
counter = JustCounter()
counter.count()
counter.count()
print counter.__secretCount
La execuția codului de mai sus, rezultatul este următorul:
1
2
Traceback (most recent call last):
File "test.py", line 11, in <module>
print counter.__secretCount
AttributeError: JustCounter instance has no attribute '__secretCount'
Python protejează membrii privați prefixându-le numele cu numele clasei din care fac parte. Așadar, încapsularea este o convenție, încurajând programatorii să fie responsabili, și nu restricționând complet accesul. În exemplul de mai sus, se poate accesa membrul privat astfel:
counter._JustCounter__secretCount
Similar unui constructor în Java sau C++, Python pune la dispoziție metoda __init__
. Aceasta primește ca prim argument o instanță a clasei (self
), și poate primi și alte argumente, care pot fi folosite pentru setarea unor atribute ale instanței.
>>> class Foo:
... def __init__(self, x):
... self.x = x
...
>>> foo = Foo('This is x')
>>> foo.x
'This is x'
Această metodă se apelează de fiecare dată când un obiect este șters din memorie, fiind similară cu un destructor.
Pentru a genera o reprezentare text a unei instanțe a unei clase se va defini metoda __str__
care primește ca argument instanța clasei și returnează un string.
>>> class Bar:
... def __init__(self, x):
... self.x = x
... def __str__(self):
... return "X = {}".format(self.x)
...
>>> bar = Bar(4)
>>> print bar
X = 4
>>> bar = Bar('Y')
>>> str(bar)
'X = Y'
Odată cu apariția Python 2.2 a fost introdus un nou mod de a defini și utiliza clasele. O clasă new style este o clasă care este derivată dintr-un built-in, cel mai adesea object
. Cea mai importantă diferență între cele două tipuri de clase ține de tipul instanțelor. O instanță a unei clase clasice va avea tipul instance
, în timp ce instanțele claselor new style vor avea tipul returnat de <nume clasă>.__class__
, ceea ce le situează pe același nivel cu clasele built-in
. Mai mult, clasele new style introduc noțiuni ca proprietate și metodă statică.
>>> class NewStyleFoo(object):
... pass
Proprietățile sunt atribute pentru care s-au definit metode setter și getter.
>>> class Student(object):
... def __init__(self):
... self.__grade = 0
...
... def get_grade(self):
... return self.__grade
...
... def set_grade(self, grade):
... self.__grade = grade
...
... grade = property(get_grade, set_grade)
>>> s = Student()
>>> s.grade
0
>>> s.grade = 10
>>> s.grade
10
În Python, acestea se comportă exact ca în C++ sau Java. Metodele statice nu folosesc parametrul self
și nu necesită instanțierea clasei în prealabil. Se definesc utilizând metoda built-in staticmethod()
.
>>> class Student(object):
... def static_info():
... print 'This is a static method.'
... info = staticmethod(static_info)
>>> Student.info()
This is a static method.
Atunci când o clasă new-style este derivată din altă clasă, aceasta are posibilitatea de a invoca clasa bază folosind metoda built-in super()
.
>>> class Bird(object):
... def __init__(self, can_fly):
... self.can_fly = can_fly
...
>>> class Parrot(Bird):
... def __init__(self, can_fly, known_words):
... super(Parrot, self).__init__(can_fly)
... self.known_words = known_words
Există o serie de funcții built-in care au sens în contextul programării orientate pe obiecte. Am exemplificat deja câteva dintre ele (property
, staticmethod
, super
) care funcționează pentru new-style classes. Cele enumerate mai jos sunt valabile atât pentru clasele new-style, cât și pentru cele old-style.
-
getattr(obj, name[, default]) - pentru a aceesa un atribut al unui obiect; returnează
default
dacă obiectul nu are atribut cu numelename
; - hasattr(obj, name) - verifică dacă obiectul are un atribut;
- setattr(obj, name, value) - setează un atribut; daca atributul nu există, este creat;
- delattr(obj, name) - șterge un atribut
Exemplu:
>>> class Pet:
... pass
...
>>> my_pet = Pet()
>>> if not hasattr(my_pet, 'name'):
... setattr(my_pet, 'name', 'Sasha')
...
>>> getattr(my_pet, 'name')
'Sasha'
>>> delattr(my_pet, 'name')
>>> getattr(my_pet, 'name', 'Unknown')
'Unknown'
-
isinstance(object, type) - retunează
True
dacăobject
este o instanță a tipuluitype
sau a oricărei subclase a tipuluitype
; -
issubclass(class, base) - returnează
True
dacă clasaclass
este subclasă a claseibase
.
Exemplu:
>>> class Animal(object):
... pass
...
>>> class Pet(Animal):
... pass
...
>>> lion = Animal()
>>> cat = Pet()
>>> isinstance(cat, Pet)
True
>>> isinstance(cat, Animal)
True
>>> isinstance(cat, object)
True
>>> isinstance(lion, Animal)
True
>>> isinstance(lion, Pet)
False
>>> issubclass(Pet, Animal)
True
>>> issubclass(Animal, Pet)
False