introducere in programare folosind python...2010/09/08 · introducere in programare folosind...
TRANSCRIPT
SPAM
Dr. Elena OvreiuUniversitatea POLITEHNICA din Bucurestiwww.ovreiu.com
INTRODUCERE IN PROGRAMARE FOLOSIND PYTHON
PYTHON 8,9,10Clase si Obiecte
Paradigme de programare
• Paradigma de programare = stil de programare. • Exista mai multe paradigme de programare: programare
procedurala, funcțională, obiect-orientata.
Programare procedurala
• Pana acum, am folosit paradigma de programare procedurala: un stil de programare in care programatorul scrie o succesiune de secvențe de instrucțiuni care duc către o soluție
• Fiecare instrucțiune schimba starea programului. • Starea unui program: valorile variabilelor unui program in
timp ce acesta rulează.
Programare proceduralax = 2
y = 4
z = 8
xyz = x+y+z
xyz
>>> 14
• Fiecare linie din acest exemplu schimba starea programului.
Programare procedurala• In programarea procedurala, datele sunt salvate in variabile
globale, care sunt manipulate cu funcții. rock=[]pop=[]def collect_songs():
while True:gen=input(="Introduceti r sau p. q pentru
închidere!")if gen=="q":
break if gen=="r":
rk=input(’Introduceti o melodie:’)rock.append(rk)
elif gen==”p":
pp=input(’Introduceti o melodie:’)
country.append(pp)
else:
print("Invalid")
print(rock)
print(country)
collect_songs()
• Programarea procedurala funcționează bine când construim programe mici. Când programele cresc in complexitate, valorile variabilelor sunt greu de urmărit. Acest lucru conduce la erori.
Programare funcționala• Folosește funcții care modifica starea variabilelor pe care le
primește ca argument. • Astfel, avem un control al modificării variabilelor. • Valoarea returnata de o funcție este pasata ca argument
unei alte funcții. • Modificarea stării globale a programului se face prin apeluri
succesive de funcții. def increment (a):
return a+1
Programarea Obiect-Orientata
• Programarea Obiect-Orientata (POO) își are rădăcinile in anii 1960, dar a devenit o paradigma de programare abia in anii ’80
• A fost dezvoltata pentru a face fata creșterii rapide in dimensiune si complexitate a sistemelor software si pentru a facilita modificarea in timp a acestor sisteme.
• Python este un limbaj de programare obiect-orientata.
Programarea Obiect-Orientata• In POO, accentul se pune pe crearea unor obiecte care
conțin: date (atribute) si metode (funcții)• Clasele de permit sa ne cream propriile noastre tipuri de
date, care sunt definite de utilizator. • Pana acum am folosit clase precum string, int, float, bool
• Clasele definesc un set de obiecte care pot interacționa intre ele.
• Clasele reprezintă un mecanism pentru ca programatorii sa grupeze obiecte similare.
Magazin Online – exemplu de obiecte
Cumparator Comanda
Produs Plata
Definiția unei clase
• Clasa Point• Consideram un punct matematic: acesta este definit de 2
numere (coordonatele punctului), care sunt considerate colectiv, ca un singur obiect.
• (0,0)reprezintă originea, (x,y) reprezintă un punct situat la x unități in dreapta si y unitati in sus fata de origine
Definitia unei clase
• Operații tipice pe care le facem asupra unui punct:o calculam distanta punctului fata de origineo calculam mijlocul distantei intre 2 puncteo etc
Definiția unei clase• Un mod natural de a reprezenta un punct este de a folosi 2
valori numerice (coordonatele pe x si y). class Point:
""" Point class represents and
manipulates x,y coords. """
def __init__(self):""" Create a new point at the origin
"""
self.x=0
self.y=0
Definiția unei clase• Definiția unei clase poate sa fie făcută oriunde in program,
dar de obicei se face aproape de început, după instrucțiunile import
• Unii programatori prefera sa pună clasele in module separate; in acest curs le vom pune in același modul
Sintaxa• Header class Point:
• cuvântul cheie class urmat de numele clasei si 2 puncte
• Alineatul ne spune unde se termina clasa
Numele unei clase
• In Python, numele unei clase începe întotdeauna cu litera mare: class Point
• Daca numele conține 2 cuvinte, in loc sa fie separate prin underscore, fiecare cuvântul începe cu litera mare: class PointTriangle
• Denumirile metodelor unei clase sunt întotdeauna cu litere mici.
Definitia unei clase
• Prima linie dupa header este un string """ Point class represents and manipulates x,y coords. ""”
• Acest string se numeste docstring (Python documentation string) si are rolul de a documenta module, functii, clase simetode. Explica ce face functia sau metoda sau clasa.
• Un docstring incepe cu litera mare si se termina cu punct. • Daca doctringul se intinde pe mai multe linii, acestea
trebuie sa fie separate printr-o linie goala.
Definitia unei clase
• Toate clasele trebuie sa aibă o metoda cu numele special __init__ care este o metoda de inițializare
• Aceasta metoda este apelata automat ori de cate ori cream o instanță a unei clase (in cazul acesta, a clasei Point)
• Aceasta oferă programatorului posibilitatea de a configura atributele necesare in noua instanță, oferindu-le setările sau valorile inițiale.
Definiția unei clase
• Parametrul self (poate fi folosit orice alt nume, dar selfeste convenția) este automat setat pentru a face referire la obiectul nou care trebuie inițializat.
p=Point()#obiect p de tip Point
q=Point()#al 2lea obiect de tip Point
print(p.x,p.y,q.x,q.y)#fiecare punct are propriul x si y
Definitia unei clase
• Programul afișează: 0 0 0 0 deoarece, in timpul inițializării obiectelor, am creat 2 atribute, x si y, cărora le-am dat valorile 0
• O funcție Point care creează o noua instanța a obiectului se numește constructor.
• Fiecare clasa oferă in mod automat o funcție constructor care are aceeași denumire cu clasa respectiva
Definiția unei clase
• Ar putea fi util sa va gândiți la o clasa ca la o fabrica ce generează obiecte.
• Clasa însăși nu este o instanța a unui punct, dar conține mecanismele care generează puncte.
• De fiecare data când apelam un constructor, ii cerem „fabricii” sa ne creeze un nou obiect.
• Când obiectul iese din „linia de producție”, metoda de inițializare este executata pentru a obține obiectul setat corespunzător cu setările implicite din fabrica.
• Procesul combinat de a face un obiect nou si de a-l initializacu „setările implicite din fabrica” se numește instantiere
Atribute
• Ca si obiectele din lumea reala, obiectele generate au atribute si metode
• Putem modifica atributele unui obiect folosind notația cu punct:
>>> p.x=3
>>> p.y=4
Atribute• Ambele instanțe își creează propriile spatii de nume, iar
sintaxa pentru accesarea numelor conținute in fiecare, numite atribute, este aceeași. In acest caz, atributul pe care il selectam este un element de date dintr-o instanța.
• Variabila p se refera la un obiect Point care conține 2 atribute. Fiecare atribut se refera la un număr.
p x 3y 4
Atribute• Putem accesa valorile unui atribut folosind aceeași sintaxa: >>>print(p.y)4
>>>x=p.x
>>>print(x)
3
• Expresia p.x inseamna: mergi la obiectul p la care facireferire si obtine valoarea lui x.
Atribute>>>x=p.x
• S-a atribuit valoarea p.x unei variabile x. • Nu exista niciun conflict intre variabila globala x si atributul x
in spațiul de nume care apartine instantei. • Scopul notației cu punct (p.x) este de a clarifica la ce
variabila facem referire. • Putem folosi si scrierea: >>> print("(x={0}, y={1})".format(p.x, p.y))
(x=3,y=4)
Atribute
>>> distance_squared_from_origin = p.x * p.x+ p.y * p.y
25
Imbunatatirea metodei de initializare• Pentru a crea un punct care are coordonatele (7,6), folosim
3 linii de cod: >>> p=Point()
>>> p.x=6
>>> p.y=7
• Pentru a evita acest lucru, putem face constructorul clasei mai general, prin adăugarea unor parametri suplimentari in metoda __init__
Imbunatatirea metodei de initializareclass Point:
""" Point class represents and manipulates x,y coords. """def __init__(self, nr1=0, nr2=0):
""" Create a new point at x, y """self.x=nr1self.y=nr2
# Other statements outside the class continue below here.• Parametrii x si y sunt optionali. Daca apelantul nu furnizeaza
argumente, vor primi valorile implicite de 0.
Imbunatatirea metodei de initializare>>>p=Point(4,2)
>>>q=Point(6,3)
>>>r=Point()
>>>print(p.x,q.y,r.x)
4,3,0
__init__ nu creaza obiectul (nu rezerva memorie pentruacesta), doar seteaza obiectul dupa setarile implicite din fabrica, dupa ce acesta este creat.
Imbunatatirea metodei de initializare
Metode• Putem adăuga metode la clasa Point, metode care sunt
operații sensibile pentru puncte. • O metoda se comporta ca o funcție dar este invocata într-o
instanța specifica. • La fel ca atributele de tip data, si metodele sunt accesate
folosind notația cu punct.
• Adăugam o noua metoda la clasa Point, distance_from_origin
class Point:
""" Point class represents and manipulates x,y coords. """
def __init__(self, x=0, y=0):
""" Create a new point at x, y """
self.x=x
self.y=y
# Other statements outside the class continue below here.
def distance_from_origin(self):
""" Compute my distance from the origin """
return ((self.x ** 2) + (self.y ** 2)) ** 0.5
>>>p=Point(3,4)
>>> p.x
Out: 3
>>> p.y
Out: 4
>>> p.distance_from_origin()
Out: 5.0
>>> q=Point()
>>> q.distance_from_origin()
Out: 0.0
Metode
• Cand se construieste o metoda, primul parametru se refera la instanta care este manipulata. Acest parametru este denumit self, prin conventie.
• Observati ca, atunci cand se apeleaza functiadistance_from_origin, nu ofera explicit un argument pentru parametrul self, acesta este facutautomat
Obiecte ca argumente si parametri
• Transmiterea unui obiect ca argument este ceva obisnuit. def print_point(pt):
print("({0},{1})".format(pt.x,pt.y))
• print_point ia un punct ca argument si afiseazacoordonatele punctului respectiv.
Conversia unui obiect intr-un string
• Mulți programatori nu vor afișa coordonatele unui punct așa cum am procedat noi.
• O abordare mai potrivita ar fi sa se adauge o metoda astfel încât orice obiect poate produce o reprezentare string a sa.
• Sa numim aceasta metoda to_string. class Point:
#...
def to_string(self):return "({0},{1})".format(self.x,self.y)
Conversia unui obiect intr-un string
>>>p=Point(3,4)>>>print(p.to_string())
(3,4)
• In loc de functia to_string() am putea sa folosim direct functia built-in din Python (str)? Este posibil, dar rezultatul va fi:
>>>str(p)
Out[20]: '<__main__.Point object at 0x11698ea20>'
Conversia unui obiect intr-un string
>>>print(p)
<__main__.Point object at 0x11698ea20>
• Putem redenumi metoda: def __str__(self):
return "({0},{1})".format(self.x,self.y)
>>>str(p)
Out: '(3,4)'
Funcții care returnează obiecte• Atât funcțiile cat si metodele pot returna obiecte.
• De exemplu, se dau 2 puncte si vrem sa returnam punctul din mijlocul lor.
def midpoint(p1,p2):
""" Return the midpoint of points p1 and p2 """
mx=(p1.x+p2.x)/2
my=(p1.y+p2.y)/2
return Point(mx,my)
Metode care returnează obiecte• Transformam functia midpoint in metoda:def midpoint(self,p):
""" Return the midpoint of points p1 and p2 """
mx=(self.x+p.x)/2
my=(self.y+p.y)/2
return Point(mx,my)
Obiecte ca valori returnate• Transformam aceasta funcție într-o metoda: presupunem ca
avem un obiect Point si dorim sa calculam distanta de mijloc dintre el si un punct target
>>> p1=Point(3,4)
>>> p2=Point(5,12)
>>> p3=p1.midpoint(p2)
>>> print(str(p3))
(4.0, 8.0)
Obiecte ca valori returnate• Putem sa folosim si alternativa fără sa folosim variabile: print(str(Point(3,4).midpoint(Point(5,12))))
(4.0, 8.0)
Dreptunghi• Sa presupunem ca ne dorim o clasa care sa reprezinte un
dreptunghi situat undeva, in planul xy. • Întrebarea este: ce informații trebuie sa furnizam pentru a
specifica un astfel de dreptunghi?• Presupunem ca dreptunghiul este orientat fie vertical, fie
orizontal.• Câteva posibilități: putem specifica centrul dreptunghiului
(coordonatele x si y), precum lungimea si lățimea acestuia; putem specifica 2 colturi opuse ale dreptunghiului.
Dreptunghi• O alegere standard este sa se specifice coltul din stânga
sus si dimensiunea dreptunghiului (lungime, lățime). • Definim o noua clasa, care va avea un inițializator si un
convertor la string.
class Rectangle:
""" A class to manufacture rectangle objects"""
def __init__(self,posn,w,h):
""" Initialize rectangle at posn, withwidth w, height h """
self.colt=posn
self.latime=w
self.lungime=h
def __str__(self):
return"({0},{1},{2})".format(self. colt,self. latime,self.lungime)
>>> dr1=Rectangle(Point(0,0),100,200)
>>> dr2=Rectangle(Point(100,80),5,10)
>>> print("dreptunghi 1: ", dr1)
>>> print("dreptunghi 2: ", dr2)
• Scrierea dr1.colt.x inseamna: mergi la obiectul dr1, selecteaza atributul lui numit colt, apoi mergi la obiectul pe care acesta il reprezinta si selecteaza atributul lui, x.
lățimelungimecolt
100200
xy
00
dr1
Obiectele sunt modificabile• Putem modifica starea unui obiect făcând o atribuire la unul
dintre atributele sale. • Exempludr1.latime+=50
dr1.lungime+=100
• Mai degrabă cream o metoda in interiorul clasei, care sa modifice dimensiunea dreptunghiului
• Putem crea o alta metoda care sa deplaseze dreptunghiul.
class Rectangle:def grow(self,delta_latime,
delta_lungime):
""" Grow (or shrink) this object bythe deltas """
self.latime+= delta_latime
self.lungime+= delta_lungime
def move(self,dx,dy):""" Move this object by the deltas
"""
self.colt.x+=dxself.colt.y+=dy
>>>r = Rectangle(Point(10,5), 100, 50)
>>>print(r)
((10,5),100,50)
>>>r.grow(25,-10)
>>>print(r)
((10,5),125,40)
>>>r.move(-10,10)
>>>print(r)
((0,15),125,40)
Is• Is compara 2 variabile. • Returnează True daca cele 2 variabile fac referire la
același obiect si False in caz contrar. class Person(self):
def __init__(self):
self.name=‘Bob’>>> bob=Person()
>>> same_bob=bob
>>>print(bob is same_bob)
True
Is>>> another_bob=Person()
>>> print(bob is another_bob)
False
• In primul caz, ambele variabile pointeaza catre acelasiobiect.
• In cel de-al 2lea caz, variabilele pointeaza catre obiecte diferite.
>>> p1 = Point(3, 4)
>>> p2 = Point(3, 4)
>>> p1 is p2False
• Chiar daca p1 si p2 au aceleasi coordonate, nu sunt aceleasiobiecte
• Daca ii atribuim p1 lui p3, atunci cele 2 variabile se refera la acelasiobiect
>>> p3=p1
>>> p1 is p3
True
• Acest tip de egalitate se numește egalitate superficiala, deoarece compara numai referințele, nu si conținutul obiectelor.
• Pentru a compara conținutul obiectelor, numita egalitate profunda, putem scrie următoarea metoda:
def same_coordinates(p1,p2):return (p1.x==p2.x) and (p1.y==p2.y)
>>> p1=Point(3,4)
>>> p2=Point(3,4)
>>> same_coordinates(p1,p2)
True
Copy• Modulul copy conține o funcție numita copy care dublează
orice obiect>>> import copy>>> p1=Point(3,4)>>> p2=copy.copy(p1)>>> p1 is p2False>>> same_coordinates (p1,p2)True• p2 nu este același punct cu p1, dar conține aceleași date (are
aceleași coordonate)
• Pentru copierea unui obiect simplu, cum ar fi un obiect de tip Point, care nu conține obiecte incorporate, metoda copy este suficienta. Aceasta se numește shallow copy
• Pentru un obiect de tip Rectangle, care contine o referința către un Point, copy nu funcționează chiar cum ne așteptam.
• In acest caz, copy copiază referința la obiectul Point, astfel încât atât noul cat si vechiul obiect de tip Rectangle se refera la un singur obiect Point
• In acest caz, daca invocam metoda grow unuia dintre obiecte ( b1 sau b2), celălalt nu va fi afectat.
• Dar daca invocam metoda move unui obiect, va fi afectat si al 2 lea
widthheightcorner
100200 x
y00
b1 widthheightcorner
100200
b2
Deepcopy
• Pentru a evita astfel de probleme, se folosește metoda deepcopy:
>>> b2=copy.deepcopy(b1)
• b1 si b2 sunt obiecte complet separate.
MyTimeclass MyTime:
def __init__(self,hrs=0,mins=0,secs=0):
""" Create a MyTime objectinitialized to hrs, mins, secs """
self.hours=hrsself.minutes=minsself.seconds=secs
MyTime• Generam un obiect de tipul MyTime:time1=MyTime(12,23,30)
def __str__(self):return "({0}:{1}:{2})".format(self.hours, self.minutes, self.seconds)
hoursà12minutesà23secondsà30
time1
MyTimeVom scrie 2 versiuni ale funcțieiadd_time,care calculează suma dintre 2 obiecte de tipul MyTime.
def add_time(t1,t2):h=t1.hours+t2.hours
m=t1.minutes+t2.minutes
s=t1.seconds+t2.seconds
sum_t=MyTime(h,m,s)
return sum_t
MyTime>>> t1=MyTime(9, 14, 30)
>>> t2=MyTime(3, 35, 0)
>>> t=add_time(t1,t2)
>>> print(str(t))
(12:49:30)
Rezultatul acestui program este corect, dar sunt situații când rezultatul nu este corect. Care sunt acele situații?
MyTime• Problema este ca aceasta funcție nu ia in considerare
cazurile când minutele sau secundele sunt mai mari de 60.
def add_time(t1,t2):h=t1.hours+t2.hours
m=t1.minutes+t2.minutess=t1.seconds+t2.seconds
if s>60:
s-=60
m+=1
if m>60:
m-=60
h+=1
sum_t=MyTime(h,m,s)
return sum_t
Modificatori• Sunt situații in care este util ca o funcție sa modifice 1 sau
mai multe obiecte pe care le primește ca parametri. • De obicei, apelantul are o referința la obiectele pe care le
transmite, astfel încât toate modificările pe care le produce funcția sunt vizibile apelantului.
• Aceste funcții se numesc modificatori.
Modificatoridef increment(t,sec):
t.seconds+=sec
if t.seconds>=60:
t.seconds-=60
t.minutes+=1
if t.minutes>=60:
t.minutes-=60
t.hours+=1
Modificatoridef increment(t,secs):
t.seconds+=secs
if t.seconds>=60:
t.seconds-=60
t.minutes+=1
if t.minutes>=60:
t.minutes-=60
t.hours+=1
Care este problema cu aceasta funcție?
Ce se întâmpla daca introducem o valoare pentru secunde >120?
def increment(t,secs):
t.seconds+=secs
while t.seconds>=60:
t.seconds-=60
t.minutes+=1
while t.minutes>=60:
t.minutes-=60
t.hours+=1
Functia este acum corecta cand secunde este un numar pozitiv si cand hours nu este mai mare decat 23!
Conversia funcției increment in metodaclass MyTime:
def increment(self,sec):
self.seconds+=sec
while self.seconds>=60:
self.seconds-=60
self.minutes+=1
while self.minutes>=60:
self.minutes-=60
self.hours+=1
Conversia funcției increment in metoda
Conversia funcției increment in metoda se face prin plasarea funcției in clasa MyTime si schimbarea primului parametru in self.
>>> t1=MyTime(9, 14, 30)
>>> t1.increment(500)
(9:22:50)
Observam ca obiectul nostru de tip MyTime este un număr in baza 60:
minutes=seconds*60
hours=seconds*3600
def to_seconds(self):
return self.hours*3600+ self.minutes*60
+self.seconds
Conversia inversaPentru a transforma din secunde in ore, minute, aplicam metoda inversa:
hours=totalseconds//3600
#(de ex. 5800//3600)
left= totalseconds%3600
minutes= left//60
seconds= left %60
• Metoda __init__ actuala ne va afișa ora exact așa cum este introdusa, chiar daca nu este corect scrisa:
t1=MyTime(5,75,67)
>>> (5:75:67)
• Putem modifica metoda __init__ astfel incat sa afiseze ora corecta, indiferent cum este introdusa.
class MyTime:
def __init__(self,hrs=0,mins=0,secs= 0):
totalseconds=hrs*3600+mins*60+secsself.hours=totalseconds//3600
left=totalseconds%3600
self.minutes=left//60self.seconds=totalseconds%60
t=MyTime(5,75,67)
>>> (6:16:7)
• Folosind metoda to_seconds() precum si noua metoda __init__(), putem rescrie metoda add_time() in urmatorul mod: class MyTime:
.......
def add_time(self,t):sec=self.to_seconds()+t.to_seconds()
return MyTime(0,0,sec)
t1=MyTime(5,75,67)
t2=MyTime(2,56,32)print(str(t1.add_time(t2)))
>>>(9:12:39)
• Metoda to_seconds() poate fi folosita si pentru a compara 2 momente de timp.
• De ex, metoda after() ne returneza True daca t1 este mai mare t2 si False in caz invers.
class MyTime:
.......
def after(self,time2):
return self.to_seconds()>time2.to_seconds()
>>>t1=MyTime(5,75,67)
>>>t2=MyTime(2,56,32)
>>>t1.after(t2)
>>>True
Supraîncărcarea operatorilor
• Unele limbaje de programare, inclusiv Python, permit ca metode care au același nume sa fie folosite pentru obiecte de tipuri diferite. Aceste metode au semnificație diferita in funcție de tipul obiectului pe care se aplica.
• De ex, operatorul + pentru clasa int face suma a 2 numere. Pentru clasa str realizează concatenarea a 2 șiruri.
• Aceasta caracteristica se numește supraîncărcarea operatorului.
• Daca avem:>>>t1=MyTime(5,75,67)
>>>t2=MyTime(2,56,32)
>>> t1+t2
t1+t2
TypeError: unsupported operand type(s) for +: 'MyTime' and 'MyTime’
• Operatorul + nu este definit pentru obiectele din clasaMyTime
Metode magice in Python• In Python exista sute de metode (numite magice) care sunt
asociate cate unui operator. • De exemplu, operatorul + este asociat metodei __add__()• 2+3 mai poate fi scrisa si: >>>(2).__add__(3)
>>>Out: 5
Metode magice in Python
• Pentru a vedea metodele si atributele unei clase, folosimfunctia dir
• >>> dir(int)
• Out:
• ['__abs__’, '__add__’, '__and__', '__bool__’, '__ceil__’, '__class__’,....]
Metode magice in Python>>> dir(MyTime)>>> Out: ['__class__’, '__delattr__', '__dict__', '__dir__', '__doc__’, '__eq__','__format__', '__ge__', '__getattribute__','__gt__','__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__','__reduce__', '__reduce_ex__', '__repr__','__setattr__','__sizeof__', '__str__', '__sub__','__subclasshook__','__weakref__','add_time','after','to_seconds']
Supraîncărcarea operatorilor
• Pentru a face posibila operatia + pentru obiecte din clasa MyTime(), metoda __add__ trebuie rescrisa.
class MyTime:
.......
def __add__(self,other):
returnMyTime(0,0,self.to_seconds()+other.to_seconds())
Supraîncărcarea operatorilor
>>>t1=MyTime(5,75,67)
>>> t2=MyTime(2,56,32)
>>> t3=t1+t2
>>> print(t3)
(9:12:39)
Supraîncărcarea operatorilor• Daca rescriem metoda __add__ pentru clasa Point:class Point:
def __add__(self,other):
return Point(self.x+other.x,
self.y+other.y)p1=Point(3,4)
p2=Point(5,7)
print(p1+p2)(8,11)
Supraîncărcarea operatorilor• Daca rescriem metoda __mul__ pentru clasa Point:class Point:
def __mul__(self,other):
return self.x*other.x+
self.y*other.yp1=Point(3,4)
p2=Point(5,7)
print(p1*p2)43
Supraîncărcarea operatorilor• Daca rescriem metoda __rmul__ pentru clasa Point:class Point:
def __rmul__(self,other):
return Point(other *self.x,
other *self.y)
p1=Point(3,4)
print(p1*2)
(6,8)
Polimorfism • Polimorfism =mai multe forme. • In programare, polimorfism înseamnă ca o funcție cu
același nume poate fi folosita pentru obiecte din clase diferite.
• De exemplu, operatia multadd primeste 3 parametri:def multadd(x,y,z=0):
return x*y+z
Polimorfism• Operațiile * si + sunt valabile atât pentru numere întregi cat
si pentru obiecte din clasa Point. • Așadar, funcția multadd funcționează cu argumente atât
numere întregi, cat si obiecte de tip Point. p1=Point(3,4)
p2=Point(5,7)print(multadd(p1,p2,2))
45
print(multadd(2,p2,p1))
(13,18)
Polimorfism• O funcție care poate sa primească argumente de tipuri diferite
se numește polimorfa. def front_and_back(front):
import copy
back=copy.copy(front)
back.reverse()
print(str(front)+str(back))
>>>my_list=[1,2,3,4]
>>>front_and_back(my_list)
[1,2,3,4] [4,3,2,1]
Polimorfism• Daca am vrea sa aplicam funcția front_and_back unui
punct si sa afișam coordonatele inverse, nu va funcționa: >>>front_and_back(p1)
back.reverse()
AttributeError: 'Point' object has no attribute 'reverse'
Polimorfism• Pentru a determina daca o funcție poate fi aplicata unui nou
tip (clasa), se aplica regula fundamentala din Python pentru polimorfism, numita regula duck typing: daca toate operatiile dintr-o functie pot fi aplicate unui tip, atunci functia poate fi aplicata tipului respectiv.
• In functia multadd, operatiile + si * exista atat in clasa int cat si in clasa Point (prin supraincarcare)
• In functia front_and_back metoda reverse nu exista in clasa Point.
Polimorfismclass Point:
def reverse(self):
(self.x, self.y)=(self.y,self.x)
>>>p1=Point(3,4)
>>>front_and_back(p1)
(3,4)(4,3)
Composition
• Folosirea unor clase in alte clase.• Clasa Rectangle conține un obiect de tip Point
Moștenire
• Proprietatea de moștenire ne permite sa cream o clasa care moștenește toate atributele si metodele unei alte clase.
• Superclasa sau clasa părinte este clasa moștenita• Clasa copil sau subclasa este clasa moștenitoare
Moștenireclass Person:
def __init__(self,fname,lname):self.firstname=fnameself.lastname=lname
def printname(self):print (self.firstname,self.lastname)
class Student(Person):pass
Moștenire >>> pers=Person('Ana','Popescu’)
>>> pers.printname()
Ana Popescu
stud=Student('Matei','Dumitrescu')
stud.printname()
>>> Matei Dumitrescu
print(stud.firstname)
>>> Matei
Moștenire
class Student(Person):
def __init__(self,fname,lname):
#properties
• Daca adăugam metoda __init__, clasa Student nuva mai moșteni metoda __init__ a clasei părinte, Student. Metoda __init__ din clasa Student suprascrie metoda __init__ din clasa Person.
Moștenire
• Pentru a păstra moștenirea clasei părinte:class Student(Person):
def __init__(self,fname,lname):
Person.__init__(self,fname,lname)
>>> stud=Student('Matei','Dumitrescu’)
>>> stud.printname()
>>> Matei Dumitrescu
Moștenire• Daca vrem sa mai adaugam atribute clasei copil: class Student(Person):
def __init__(self,fname,lname,year):
Person.__init__(self,fname,lname)
self.graduationyear=year
>>> stud=Student('Matei','Dumitrescu’, 2019)
>>> stud.printname()
>>> Matei Dumitrescu
Moștenire• Daca vrem sa mai adaugam o metoda clasei Student: class Student(Person):
def __init__(self,fname,lname,year):
Person.__init__(self,fname,lname)
self.graduationyear=year
def welcome(self):
print(‘Welcome’, self.firstname, self.lastname, ’to the class of’, self.graduationyear)
Moștenire
• Daca adăugam o metoda in clasa-copil care are același nume cu o metoda din clasa-părinte, metoda din clasa-copil o suprascrie pe cea din clasa-părinte.
Încapsularea datelor• Limbajele clasice obiect-orientate, precum C++ si Java,
controlează accesul la resursele claselor prin cuvintele cheie public, private sau protected
• Datele sau metodele private ale unei clase nu pot fi accesate fin afara clasei. Pot fi accesate numai din interiorul clasei.
• Datele /metodele protected pot fi accesate din interiorul clasei precum si din clasele care moștenesc clasa respectiva
• Aceasta clasificare in public, private si protected se numește încapsulare
Încapsularea datelor -Python• Python nu are un mecanism eficient de protejare a datelor;
in Python toate datele si metodele sunt publice. • Mecanismul prin care Python semnalează ca o data este protected sau private este prin underscore sau double underscore in fata numelui variabilei.
class Employee:
def __init__(self, name, sal):self.name=name
self.salary=sal
Încapsularea datelor -Python>>>e1=Employee('Adrian', 1200)
>>>print(e1.salary)
1200
>>>e1.salary=2000
>>>print(e1.salary)
2000
Încapsularea datelor -Python• Conventia in Python de a face o variabila protected este
de a adauga prefixul _ (underscoore) in fata numeluivariabilei
class Employee:
def __init__(self, name, sal):
self._name=name #protected attribute
self._salary=sal#protected attribute
Încapsularea datelor -Python>>>e1=Employee('Adrian', 1200)
>>>print(e1.salary)
print(e1.salary)
AttributeError: 'Employee' object has no attribute 'salary'
Încapsularea datelor -Python• Conventia in Python de a face o variabila private este de
a adauga prefixul __ (double underscoore) in fata numeluivariabilei
class Employee:
def __init__(self, name, sal):
self.__name=name #private attribute
self.__salary=sal #private attribute