Programació orientada a objectes amb Python¶

Programació orientada a objectes¶
Anteriorment has aprés com estructurar els teus projectes utilitzant funcions per descompondre-los en parts més simples. Encara que aquesta és una bona pràctica el manteniment d’una col·lecció massa gran de funcions és una tasca complicada. Per aquest motiu els informàtics han desenvolupat una nova metodologia anomenada programació orientada a objectes, en la qual els programes són col·leccions d’objectes que interactuen entre sí per a resoldre un problema. Ja has treballat amb objectes com les llistes i les cadenes, però el que aprendràs ara és a crear els teus propis objectes.
Totes les llistes són objectes que es comporten de la mateixa manera, i poden respondre als mateixos mètodes:
llistaDeVocals = ['a','i','e','o','i','u','a','e','a','i','a']
quantitatDeA = llistaDeVocals.count('a')
llistaDeVocals.sort()
llistaDeNombres = [1,5,2,3,7,8,4,2,6,9,2,5]
quantitatDe2 = llistaDeNombres.count('2')
llistaDeNombres.sort()
Una classe en Python descriu a tots els objectes del mateix tipus, com per exemple la clase list
. Els objectes individuals s’anomenen instàncies. En l’exemple anterior llistaDeVocals i llistaDeNombres són instàncies de la clase list
. Tots els objectes d’una mateixa classe (les seues instàncies) proporcionen el mateix conjunt de mètodes, el qual s’anomena la seua interfície pública. Per exemple, tant llistaDeVocals com llistaDeNombres tenen els mètodes count() i sort().
Quan treballem amb un objecte i utilitzem la seua interfície pública en realitat no sabem com emmagatzema les dades, ni com el mètodes fan el que fan. Proporcionar una interfície pública però amagar la manera concreta com està implementada és una de les característiques de la programació orientada a objectes i s’anomena encapsulació. L’encapsulació permet que els programadors facen millores dels seus objectes, per tal que tinguen més capacitats o siguen més eficients, però mentre la seua interfície pública no canvie els usuaris d’aquests objectes es beneficiaran de les millores sense tenir que canviar els seus programes.
Seguint l’exemple anterior, les llistes poden ordenar els seus elements amb el mètode sort(). Nosaltres utilitzem el mètode sort() sense saber realment com fa el seu treball i si en una nova versió de Python el mètode millora i fa el matèix més ràpidament nosaltres seguirem utilitzant-lo exactament igual.
Un exemple en la vida real podrien ser els cotxes. Els «cotxes», en general, seríen un classe mentre que cada cotxe particular sería una instància de la classe. Els comandaments (volant, pedals, botóns,…) són la seua interfície pública, que nosaltres utilitzem sense saber realment els detalls interns del seu funcionament. Com que nosaltres solament coneguem l’interfície pública, pot ocorrer que aparega un nou motor més eficient o fins i tot que el motor de gasolina siga substituit per un elèctric i com que l’interfície pública no canvia podrem seguir conduïnt sense problemes.
Implementació d’una classe¶
Anem a crear una classe que ens va a servir com exemple de com aquestes desen les dades i com funcionen els mètodes. Aquesta primera classe serà un comptador. En el món real aquests comptadors són menuts dispositius mecànics amb un botó i una pantalla. Cada vegada que es prem el botó el nombre que apareix a la pantalla augmenta en un, de manera que es pot utilitzar per comptar les persones que accedeixen a algun lloc.

L’interfície pública d’una classe¶
Quan dissenyem una classe el primer que cal tenir clar és la seua interfície pública. Aquesta interfície pública consisteix en tots els mètodes que podem utilitzar per comunicar-nos amb una instància d’eixa classe.
El nom de la nostra classe serà Counter
i tindrà quatre mètodes:
reset()
representa el botó per ficar el comptador a 0.click()
representa el botó per incrementar el comptador.getValue()
ens retornarà el valor actual del comptador. Per tant, ens permet consultar-lo.setValue()
que estableix el valor actual del comptador a un nombre concret. Per tant, ens permet modificar-lo.
Quan dissenyem l’interfície pública d’una classe són molt útils dos tipus concrets de mètodes:
Els mètodes d’actualització són els que modifiquen l’objecte, establint el valor d’alguna de les seues variables d’instància. També s’anomenen setters, i
setValue()
és un exemple.Els mètodes d’accés que retornen el valor d’una variable d’instància. També s’anomenen getters, i
getValue()
és un exemple.
A més dels mètodes, haurem de definir com els objectes emmagatzemen les seues dades. En aquest cas una variable de tipus int serà suficient.
Una vegada definida la classe podrem crear tantes instàncies de la classe Counter
com siguen necessaries. Cadascuna de les instàncies tindrà les seues pròpies variables internes que emmagatzemaran valors diferents, ja que, per exemple, no tots els comptadors existents mostraran el mateix nombre. Les diferents variables internes de cadascun dels objectes s’anomenen variables d’instància.
Nota
Alguns programadors segueixen el conveni de fer començar amb un caràcter subratllat les variables d’instància. Així per exemple anomenariem a la variable que emmagatzemarà el valor del comptador _value i ens refeririem a ella com a self._value des de dins de l’objecte o objecte._value des de fora. Aquesta nomenclatura no té cap efecte real i serveix per recordar als programadors que potser no és bona idea manipular eixa variable directament i que caldria utilitzar els getters i els setters.
Per tal de simplificar l’escriptura dels programes nosaltres no seguirem eixe conveni.
Ara ja podem començar a escriure la nostra classe i els seus mètodes. Definir els mètodes d’una classe és molt similar a escriure funcions amb dos diferències:
Els mètodes es troben sempre dins de la definició d’una classe.
El primer paràmetre d’un mètode és sempre el propi objecte representat per la paraula clau self.
Ara imagina que creem dos objectes de classe Counter
. Cadascun d’ells tindrà una variable anomenada value i un mètode reset() que establirà el seu valor a 0. Com que existeixen dos objectes i dos variables anomenades value, com indicarem que la variable que cal modificar és la del pròpi objecte i no la que s’anomena igual en un objecte diferent? Per referir-nos a les variables d’instància del mateix objecte on és invocat el mètode utilitzarem aquesta paraula clau self. Ho pots veure en el codi sencer de la classe Counter
.
1#
2# Definició de la classe Counter.
3#
4
5## Representa un comptador que pot ser incrementat, consultat o restablert.
6#
7class Counter :
8
9 ## Retorna el valor actual del comptador.
10 #
11 def getValue(self):
12 return self.value
13
14 ## Estableix el valor actual del comptador.
15 #
16 def setValue(self, n):
17 self.value = n
18
19 ## Avança el comptador una unitat
20 #
21 def click(self):
22 self.value = self.value + 1
23
24 ## Restableix el comptador a 0.
25 #
26 def reset(self):
27 self.value = 0
Normalment les definicions de classes es troben en fitxer distints dels programes on son utilitzats, anomenats mòduls. Desarem la definició de la classe Counter en un fitxer anomenat counter.py
. Ara escriurem en un fitxer diferent un programa que importarà la definició de classe i la utilitzarà.
1#
2# Programa de demostració de la classe Counter
3#
4
5# Importem la classe Counter del mòdul counter
6from counter import Counter
7
8# Creem dos instàncies de la classe Counter()
9comptadorBus = Counter()
10comptadorMetro = Counter()
11
12# Re-establim els dos comptadors.
13comptadorBus.reset()
14comptadorMetro.reset()
15
16# Utilitzem els comptadors
17comptadorBus.click()
18comptadorBus.click()
19comptadorBus.click()
20comptadorBus.click()
21
22comptadorMetro.setValue(10)
23comptadorMetro.click()
24comptadorMetro.click()
25
26# Mostrem la informació emmagatzemada en cadascun dels comptadors
27usuarisBus = comptadorBus.getValue()
28print('Bus: ', usuarisBus)
29
30usuarisMetro = comptadorMetro.getValue()
31print('Metro: ', usuarisMetro)
Constructors¶
Observa el següent exemple:
from counter import Counter
comptador = Counter()
comptador.click()
Aquest programa donarà un error, ja que quan el comptador tracta de modificar el valor de la variable self.value es troba amb que aquesta variable no existeix. Abans de començar a utilitzar el comptador cal utilitzar el mètode reset()
, que estableix el valor de la variable self.value a 0, però també la crea si no existeix. Per simplificar açò s’utilitzen els constructors.
Un constructor és un mètode que s’executara immediatament després de que la instància siga creada i s’encarregarà de crear i inicialitzar variables. El llenguatge Python utilitza el nom __init__
(amb dos subratllats abans i després) per al mètode constructor de classe.
El constructor de la nostra classe Comptador
pot ser així:
1#
2# Definició de la classe Counter.
3#
4
5## Representa un comptador que pot ser incrementat, consultat o restablert.
6#
7class Counter :
8
9 ## Constructor
10 #
11 def __init__(self):
12 self.value = 0
13
14 ## Retorna el valor actual del comptador.
15 #
16 def getValue(self):
17 return self.value
18
19 ## Estableix el valor actual del comptador.
20 #
21 def setValue(self, n):
22 self.value = n
23
24 ## Avança el comptador una unitat
25 #
26 def click(self):
27 self.value = self.value + 1
28
29 ## Restableix el comptador a 0.
30 #
31 def reset(self):
32 self.value = 0