0% au considerat acest document util (0 voturi)
76 vizualizări8 pagini

Poo Programare Orientata Ob

Documentul prezintă principiile de bază ale programării orientate pe obiecte în Python, inclusiv definirea de clase și obiecte, metode, atribute, mostenire și spațiul de nume.

Încărcat de

istef2010
Drepturi de autor
© © All Rights Reserved
Respectăm cu strictețe drepturile privind conținutul. Dacă suspectați că acesta este conținutul dumneavoastră, reclamați-l aici.
Formate disponibile
Descărcați ca PDF, TXT sau citiți online pe Scribd
0% au considerat acest document util (0 voturi)
76 vizualizări8 pagini

Poo Programare Orientata Ob

Documentul prezintă principiile de bază ale programării orientate pe obiecte în Python, inclusiv definirea de clase și obiecte, metode, atribute, mostenire și spațiul de nume.

Încărcat de

istef2010
Drepturi de autor
© © All Rights Reserved
Respectăm cu strictețe drepturile privind conținutul. Dacă suspectați că acesta este conținutul dumneavoastră, reclamați-l aici.
Formate disponibile
Descărcați ca PDF, TXT sau citiți online pe Scribd
Sunteți pe pagina 1/ 8

DokuWiki

Programare Orientată Obiect


Prezentare: py4school_-_poo.pdf

În capitolul anterior am văzut cum declarăm funcții individual, pentru a lucra într-o manieră procedurală. În continuare ne vom referi la organizarea acestor funcții în
cadrul unor clase, programare orientată pe obiecte (OOP).

Vom prezenta pe scurt cum se utilizează clasele, obiectele, moștenirea.

Pentru o scurtă introducere despre modul de funcționare al claselor și obiectelor, precum și o diferențiere clara între programarea (clasică) procedurală și cea
orientată pe obiect, consultati paragraful de aici [http://ro.wikipedia.org/wiki/Programare_orientat%C4%83_pe_obiecte#Clase_.C8.99i_obiecte].

Clase și obiecte
E important de stiut ca totul in python este un obiect. O functie de exemplu este tot un obiect, sa vedem rapid de ce:

def a():
"""Un text descriptiv al functiei"""

pass
>>> a.__doc__

'Un text descriptiv al functiei'

Asadar vedeti ca o functie are anumite atribuite precum __doc__, si multe altele interne (dir(a) va arata alte metode interne ale obiectului a). E important sa retineti ca
totul este un obiect.

Definirea unei clase

Se definește prin cuvântul rezervat class. Este recomandat ca numele clasei să fie cu litere mari, dar nu este o eroare dacă numiți o clasă cu litere mici.

class NumeClasa:
"""Adăugarea unei descrieri (doc string) despre scopul clasei este recomandat."""

<declaratie-1>

<declaratie-N>

Codul definit in cadrul unei clase trebuie, ca si in cadrul functiilor (cu declaratia def), trebuie executat inainte sa aiba vreun efect. O clasa are propriul namespace (spatiu
de nume), deci orice atribuire de variabile in cadrul unei clase intra in spatiul de nume local acelei functii.

Obiectul Clasa

class MyClass:

"""Un exemplu de clasa simpla"""

i = 12345

def f(self):
return 'hello world'

Apoi MyClass.i si MyClass.f sunt referentieri valide (returnand un intreg si respectiv un obiect de tip functie), pentru ca atributele i si f au fost definite in spatiul de nume
al clasei MyClass. Putem atribui alte valori variabilelor claselor, deci putem schimba valoarea lui MyClass.i daca dorim.

>>> MyClass.i

12345

>>> MyClass.f

<unbound method MyClass.f>

>>> MyClass.i = 1

>>> MyClass.i

>>> MyClass.__doc__

'Un exemplu de clasa simpla'

Instantierea unei clase

Instantierea unei clase in python se face ca apelul unei functii. Exemplul de cod:

>>> x = MyClass()

>>> x.i

>>> x.f

<bound method MyClass.f of <__main__.MyClass instance at 0x10049cd40>>

creeaza o noua instanta a clasei si atribuie acest obiect unei variabile locale x. Observati ca atributul i este un atribut al clasei, deci va fi partajat tuturor instantelor clasei.

Instantierea va produce un obiect x gol insa, fara variabile interne proprii. De multe ori clasele vor sa personalizeze obiectele create cu o stare initiala anume. Astfel o clasa
poate defini metoda speciala __init__:
def __init__(self):

self.data = []

Cand o clasa defineste o metoda __init__, instantierea clasei apeleaza aceasta metoda. Aceasta metoda poate avea orice numar de argumente, bineinteles:

class Complex:

def __init__(self, realpart, imagpart):

self.r = realpart

self.i = imagpart

>>> complex = Complex(3.0, -4.5)


>>> complex.r, complex.i
(3.0, -4.5)

Metode și atribute ale clasei

Reluam exemplul

class MyClass:

"""Un exemplu de clasa simpla"""

i = 12345

def f(self):
return 'hello world'

In python, o instanta poate referi doua tipuri de atribute: atribute de date si metode.

Atributele de date nu trebuie declarate inainte, ele sunt create atunci cand le este atribuita o valoare prima oara. De exemplu, daca folosim instanta x a clasei MyClass,
codul urmator va printa 16 fara a lasa vreo urma (pentru ca in final stergem atributul counter abia creat):

>>> x.counter = 1

>>> while x.counter < 10:

x.counter = x.counter * 2

>> print x.counter

16

>> del x.counter


>> print x.counter

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

AttributeError: MyClass instance has no attribute 'counter'

Al doilea tip de atribut este o metoda, care nu este altceva decat o functie ce apartine unui obiect. Asta inseamna ca o metoda trebuie sa contina cumva si o referinta catre
obiect.

O functie pe o clasa determina la randul sau o metoda in cadrul unei instante a acelei clase. Deci MyClass.f fiind o functie, determina sa existe o metoda pe x.f, in timp ce
x.i nu este o metoda, pentru ca nici MyClass.i nu este. x.f este o metoda obiect, in timp ce MyClass.f este o functie obiect.

Metode obiect

O metoda poate fi chemata imediat, iar in exemplul anterior x.f() ar produce 'hello world'. Putem insa amana acest lucru pentru mai tarziu:

xf = x.f
while True:

print xf()

va continua sa printeze “hello world” pentru totdeauna. De ce putem face asta?

Cand o metoda obiect este apelata, obiectul (referinta catre obiect) este pasat automat de catre python pentru noi. Astfel ca x.f() fiind o metoda, este perfect echivalent cu
a face MyClass.f(x), care este apelul unei functii, dar careia ii pasam noi manual referinta catre acel obiect.

>>> x.f()

'hello world'

>>> MyClass.f(x)
'hello world'

Acel cuvant self din cadrul unei metode reprezinta chiar instanta curenta ce este pasata automat de python cand apelam o metoda obiect. Ca o analogie, poate fi acelasi
lucru ca this din C++. Numele de self este o conventie, insa prin nerespectarea ei, codul poate deveni mai greu de inteles si de citit.

Instantiere clasa, constructor __init__, atribute instanta


class Persoana:

def __init__(self, nume):

self.nume = nume
def salut(self):
print('Salut, numele meu este %s.' % self.nume)

>>> p = Persoana()

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

TypeError: __init__() takes exactly 2 arguments (1 given)

>>> p = Persoana('Gigel')

>>> p.salut()

Salut, numele meu este Gigel.

Daca incercam sa instantiem clasa fara parametrul nume, vom primi o eroare.
Aici atributul nume este la un atribut al instantei, fiind propriu obiectului, nu clasei (spre
deosebire de MyClass.i).

Manipularea unui obiect


Fiindca o clasa este si ea un obiect, aceste lucruri se aplica si claselor.

class MyClass:

i = 'ceva'

>>> MyClass.i

'ceva'

>>> del MyClass.i

>>> MyClass.i

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

AttributeError: class MyClass has no attribute 'i'

>>> MyClass.j = 'altceva'

>>> MyClass.j

'altceva'

# Putem defini o functie si apoi s-o atribuim clasei.

# Observati ca trebui sa primeasca parametrul self.

def f1(self, x, y):

return min(x,y)

>>> MyClass.minim = f1

>>> a = MyClass()

>>> a.minim(2,3)
2

>>> a.minim = 'schimbam functia cu un string'

>>> a.minim

'schimbam functia cu un string'

# Insa atributul clasei ramane tot functia f1, nu e schimbat in string;

# ce se intampla? vedem imediat mai jos.

>>> MyClass.minim

<unbound method MyClass.f1>

Practic trebui sa retineti ca atat clasele cat si instantele nu sunt altceva decat niste dictionare, deci pot fi modificate oricand.

Spatiul de nume al atributelor

Vom defini o instanta a clasei MyClass, pentru a vedea distinctiile intre atribute ale clasei si cele ale instantei. Vom folosi metoda vars() pentru a inspecta ce atribute locale
contine un obiect.

class MyClass:

i = 1

>>> MyClass.i

>>> vars(MyClass)

# Observati ca numele cu dublu underscore sunt atribute interne.

{'i': 1, '__module__': '__main__', '__doc__': None}

>>> a = MyClass()

>>> a.i

>>> vars(a)

{}

Ce se intampla aici? Vedem ca MyClass contine atributul i, in timp ce instanta a nu il contine. Cu toate astea a.i afisaza valoarea corecta.

Pentru a intelege mai bine, sa incercam sa modificam variabila clasei, iar apoi a instantei:

>>> MyClass.i += 1

>>> MyClass.i, a.i

(2, 2)

>>> vars(a)

{}

>>> a.i = 42
>>> vars(a)

{'i': 42}

>>> vars(MyClass)

{'i': 2, '__module__': '__main__', '__doc__': None}

Deci putem vedea ca instanta a tocmai fiindca nu avea un atribut cu numele i, incearca sa il gaseasca in cadrul clasei MyClass. Insa odata ce am creat un atribut i pe
instanta cu a.i = 42, obiectul va gasi valoarea in atributele interne, si nu va mai cauta pe atributul clasei (care a ramas neschimbat, observati!).

Un ultim exemplu ar trebui sa faca tot mai clara distinctia intre atributele claselor si cele ale obiectelor.

class A:
def __init__(self):

self.i = 2

>>> vars(A)

{'__module__': '__main__', '__doc__': None, '__init__': <function __init__ at 0x1004a32a8>}

>>> a = A()

>>> vars(a)

{'i': 2}

Mostenire

Un avantaj al OOP este reutilizarea codului. Putem face asta usor cu


mostenirea claselor.

class Persoana(object):

def __init__(self, nume):

self.nume = nume
def salut(self):
print('Salut, numele meu este %s.' % self.nume)

# Mostenirea se face prin ClasaNoua(ClasaVeche), unde ClasaNoua extinde

# ClasaVeche.

class Profesor(Persoana):

def __init__(self, nume, liceu):


# Apelare constructor pentru Persoana va

# seta numele pe instanta.

Persoana.__init__(self, nume)

self.liceu = liceu

def preda(self):
print('Voi preda python in liceul %s.' % self.liceu)

In python extindem o clasa folosind paranteze, ca in exemplul in care Profesor extinde Persoana.

Observati cum clasa Persoana extinde clasa object. Este din nou o recomandare
in python ca clasa de baza sa extinda object.

Totodata important de observat este ca python NU va apela automat


constructorul clasei de baza (pe cel al clasei Persoana), trebuie sa facem noi asta. Iar
in acest caz
trebuie pasat si parametrul self, in Persoana.__init__(self, nume).

>>> profesor = Profesor('Andrei', 'Sincai')

# Profesor extinde clasa Persoana, deci are acces si la metoda salut.

>>> profesor.salut()
Salut, numele meu este Andrei.

# Metoda definita in cadrul clasei Profesor.

>>> profesor.preda()
Voi preda python in liceul Sincai.

In aceasta metoda nu e nevoie sa pasam noi parametrul self, si nici sa


stim pe cine extinde clasa Profesor.

Functii builtin
Vom folosi urmatorul cod pentru demonstrare. pass ne permite sa nu definim conținutul unei clase sau al unei functii, altfel am primi erori fiindca python se bazeaza pe
indentare, nu putem lasa locul gol complet.

class Person(object):

pass
 

class Person1(Person):

pass
 

class Person2(Person):

pass

isinstance

isinstance(object, type) → boolean

Adevarat daca object este o instanta a tipului type sau a oricarei subclase a tipului type.

>>> isinstance(Person(), Person)


True
>>> isinstance(Person1(), Person)

True
>>> isinstance(Person1(), Person2)

False

>>> isinstance(Person(), Person1)

False

issubclass

issubclass(class, base) → boolean

Adevarat daca clasa class este subclasa a clasei base.

>>> issubclass(Person1, Person)

True
>>> issubclass(Person, Person1)

False

>>> issubclass(Person1, Person2)


False

super

super(type) → type

Va returna superclasa tipului type. Mai multe detalii aici [http://rhettinger.wordpress.com/2011/05/26/super-considered-super/].

class A(object):
def show(self):

print 'show from A()'

class B(A):

def show(self):

print 'show from B()'

>>> A().show()

show from A()

>>> B().show()

show from B()

>>> super(B, B()).show()


show from A()

Duck Typing
Acest concept se refera la modul dinamic de “tipare” (dynamic typing) in python, in care metodele si proprietatile unui obiect determina semantica acestuia, si nu
mostenirea. Pe scurt, “nu verifica daca este o rata, verifica daca face ca o rata, daca merge ca o rata etc.”. Mai multe pe Wikipedia [https://en.wikipedia.org/wiki/Duck_typing].

class Duck:

def quack(self):
print "Quaaaaaack!"

def feathers(self):

print "Rata are pene albe si gri."

class Person:

def quack(self):
print "Persoana imita rata."

def feathers(self):

print "Persoana ia o pana de pe jos si o arata."

def in_the_forest(duck):

duck.quack()
duck.feathers()

Si dupa ce definim doua clase, Duck si Person care ambele implementeaza aceleasi metode quack si feathers, putem observa faptul ca metodei in_the_forest nu-i pasa ce
tip de obiect/instanta primeste, atata timp cat are acele metode implementate.

>>> donald = Duck()

>>> in_the_forest(donald)

Quaaaaaack!

Rata are pene albe si gri.

>>> john = Person()

>>> in_the_forest(john)

Persoana imita rata.

Persoana ia o pana de pe jos si o arata.

Alte resurse
Alte materiale:

Documentația oficială python [http://docs.python.org/2/tutorial/classes.html].


http://swaroopch.com/notes/python_en-object_oriented_programming/ [http://swaroopch.com/notes/python_en-object_oriented_programming/]
http://anandology.com/python-practice-book/object_oriented_programming.html [http://anandology.com/python-practice-book/object_oriented_programming.html]
http://en.wikibooks.org/wiki/Python_Programming/Classes [http://en.wikibooks.org/wiki/Python_Programming/Classes]
http://www.freenetpages.co.uk/hp/alan.gauld/tutclass.htm [http://www.freenetpages.co.uk/hp/alan.gauld/tutclass.htm]

Exercitii
1. Dandu-se aceste definitii de scos si de adaugat bani la un cont bancar, pastrati un comportament similar prin folosirea paradigmei de programare OOP. Definiti o clasa
numita BankAccount.

def make_account():

return {'balance': 0}

def deposit(account, amount):

account['balance'] += amount
return account['balance']

def withdraw(account, amount):

account['balance'] -= amount
return account['balance']

>>> a = make_account()

>>> b = make_account()

>>> deposit(a, 100)

100

>>> deposit(b, 50)

50

>>> withdraw(b, 10)

40

>>> withdraw(a, 10)

90

Soluție aici [http://anandology.com/python-practice-book/object_oriented_programming.html#classes-and-objects]

2. Peste BankAccount sa adaugam o clasa ChargingAccount care adauga o taxa in plus (de 3 lei sa zicem) la fiecare retragere de numerar. Ar trebui sa se comporte:

class ChargingAccount(BankAccount):

def __init__(self):

# TODO

def withdraw(self, amount):

# TODO

>>> a = ChargingAccount()

>>> a.deposit(100)

100

>>> a.withdraw(10)

87 # am fost taxati 3 lei in plus

>>> b = BankAccount()

>>> b.deposit(50)

50

>>> b.withdraw(10)

40

Soluție
class ChargingAccount(BankAccount):

def __init__(self):

BankAccount.__init__(self)

self.fee = 3
 

def withdraw(self, amount):

# Taxeaza la retragere cu o taxa in plus de 3.

return BankAccount.withdraw(self, amount+self.fee)

3. Stiind ca o clasa Point ce primeste doua coordonate (x, y) are metode interne pentru operatii aritmetice (__add__, __sub__), definiti aceste metode astfel incat sa putem
aduna/scadea doua puncte.

class Point(object):
def __init__(self, x, y):

pass
def __add__(self, other):

pass
def __sub__(self, other):

pass
 

# Ar trebui sa se comporte astfel:

>>> p1 = Point(0,1)

>>> p2 = Point(1,2)

>>> p3 = p1+p2

>>> p3

<__main__.Point object at 0x1004a4e10>

>>> p3.x
1

>>> p3.y
3

Soluție (se bazează pe exemplul cu RationalNumber [http://anandology.com/python-practice-book/object_oriented_programming.html#special-class-methods]):


class Point(object):
def __init__(self, x, y):

self.x = x

self.y = y

def __add__(self, other):

return Point(self.x + other.x, self.y + other.y)


def __sub__(self, other):

return Point(self.x - other.x, self.y - other.y)

4. Implementați clasa Complex care definește un număr complex. Clasa va permite realizarea următoarelor operații:

>>> x = Complex(2, 3)

>>> y = Complex(3, 6)

>>> x + y

5 + 9i

>>> y - x

1 + 3i

>>> x / y

0 + 0i

>>> x = Complex(2.0, 3.0)

>>> y = Complex(3.0, 6.0)

>>> x / y

0.0329218106996 + 0.00411522633745i

>>> abs(x)

3.1462643699419726

>>> x * y

-12.0 + 21.0i

>>>

Soluție (se bazează pe exemplul cu RationalNumber [http://anandology.com/python-practice-book/object_oriented_programming.html#special-class-methods]):


class Complex:

def __init__(self, real, imaginar = 0):

self.real = real
self.imaginar = imaginar
 

def __add__(self, other):

return Complex(self.real + other.real, self.imaginar + other.imaginar)

def __sub__(self, other):

return Complex(self.real - other.real, self.imaginar - other.imaginar)

def __mul__(self, other):

real = self.real * other.real - self.imaginar * other.imaginar

imaginar = self.real * other.imaginar + self.imaginar * other.real

return Complex(real, imaginar)

def __div__(self, other):

other_conj = other.real ** other.imaginar

real = (self.real * other.real + self.imaginar * other.imaginar) / other_conj

imaginar = (self.real * other.imaginar - self.imaginar * other.real) / other_conj

return Complex(real, imaginar)

def __abs__(self):

return self.real ** .5 + self.imaginar ** .5


 

def __str__(self):

return "%s + %si" % (self.real, self.imaginar)

__repr__ = __str__

5. Avand o clasa Persoana, faceti in asa fel incat atunci cand este printata sau reprezentata la consola instanta unei clase, sa afiseze un mesaj ca cel de mai jos. In mod
implicit python va afisa ceva de genul <__main__.Person object at 0x1004a94d0>.

class Person(object):

def __init__(self, name):

pass
#TODO

>>> p = Person('gicu')

>>> p

My name is gicu

>>> print p

My name is gicu

Soluție (se bazează pe exemplul cu RationalNumber [http://anandology.com/python-practice-book/object_oriented_programming.html#special-class-methods]):


class Person(object):

def __init__(self, name):

self.name = name
def __str__(self):

return 'My name is %s' % self.name

__repr__ = __str__

6. Implementati o clasa ale caror instante sa se comporte ca mai jos.

(Hint: citi mai multe despre __call__ aici [http://docs.python.org/2/reference/datamodel.html#object.__call__], aici [http://stackoverflow.com/questions/111234/what-is-a-callable-
in-python])

class Persoana(object):

def __init__(self, name):

pass
# TODO

>>> p = Persoana('Gigel')

>>> p()

Da? Ma numesc Gigel

Soluție
class Persoana(object):

def __init__(self, name):

self.name = name
def __call__(self):

print('Da? Ma numesc %s' % self.name)

7. Creati o clasa `Zar` care intoarce un numar random intre 1 si 6 la apelarea metodei `roll`. Creati o alta clasa `ZarNecinstit` care intoarce intotdeauna ceva intre 1 si 6 care
nu e random, ci fix, la apelarea metodei `roll`. Creati o lista de 5 zaruri atat corecte si incorecte si iterati pe ele apeland metoda `roll` (duck typing).

Bonus!

8. Implementat o clasa care de oricate ori e instantiata, se va intoarce aceeasi instanta mereu (singleton).
Pentru asta ar fi bine sa cititi ce este un __metaclass__ in python
aici [http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python].

class Person(object):

# TODO

>>> p1 = Person()

>>> p2 = Person()

>>> p1

<__main__.Person object at 0x1004a94d0>

# Aceeasi zona de memorie 0x1004a94d0!

>>> p2

<__main__.Person object at 0x1004a94d0>

>>> p1 == p2
True

Soluție
class Singleton(type):

_instance = {}

def __call__(cls, *args, **kwargs):

if cls not in cls._instance:

cls._instance[cls] = super(Singleton, cls).__call__(*args, **kwargs)

return cls._instance[cls]

class Person(object):

__metaclass__ = Singleton

poo.txt · Last modified: 2013/12/27 17:32 by mihait

S-ar putea să vă placă și