introducere in programare folosind python...2010/09/08  · introducere in programare folosind...

Post on 06-Mar-2021

51 Views

Category:

Documents

5 Downloads

Preview:

Click to see full reader

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

top related