Un dels principals avantatges de la programació orientada a objectes és la reutilització de codi. I una de les maneres en que es pot aconseguir és mitjançant el mecanisme d'herència. Una manera clara d'entendre l'herència és veure-la com la implementació de la relació entre tipus i subtipus entre classes.
Suposem que volem escriure un programa que ha de guardar informació dels professors i alumnes d'una escola. Tant els professors com els alumnes comparteixen característiques en comú com ara el nom, l'edat i l'adreça. Per altra banda, també tenen característiques específiques. Per exemple, els professors tenen un sou i imparteixen unes classes, mentre que els alumnes tenen notes.
Podríem crear dues classes independents per cadascun dels dos tipus. Això implicaria que afegir una nova característica comú requeriria fer-ho amb totes dues classes. A mida que els problemes es fan més complexos, això es torna immanejable.
Es podria fer millor creant una classe comuna anomenada Persona
i fer que després les classes professor i estudiant heretin
d'aquesta classe. És a dir, les segones passarien a ser subtipus de la primera.
Així, només caldria afegir les característiques particulars dels subtipus.
La classe Persona
en aquest context seria la
classe base o també superclasse.
Les classes Professor
i Alumne
serien classes derivades o subclasses.
Hi ha molts avantatges amb aquesta darrera aproximació. Si canviem o afegim
funcionalitats a la classe Persona
, automàticament
aquests canvis es reflecteixen en els seus subtipus. Per exemple, podríem afegir
un nou camp per guardar el número d'identificació escolar tant per professors
com per alumnes. Només caldria afegir-lo a la classe Persona.
Per altra banda, els canvis que fem a un dels subtipus no afecten a la resta
dels subtipus.
Un altre avantatge és que podem referir-nos als objectes de professors i
d'estudiants com a objectes de persona, cosa que ens pot ser útil en algunes
situacions com ara per a comptar el nombre de persones que hi ha a l'escola.
Això es coneix com a polimorfisme, i es
produeix quan una subclasse pot ser substituïda a qualsevol situació on s'esperi
la superclasse. És a dir, l'objecte pot ser tractat com
si fos una instància de la superclasse.
Veiem també que reusem el codi de la superclasse, i que no ens cal repetir-ho a cada subclasse, a diferència del que hagués passat si ho haguéssim resolt amb classes independents.
Veiem com funciona tot plegat amb un exemple.
Example 11.5. Ús de l'herència
#!/usr/bin/python # Filename: inherit.py class Persona: '''Representa qualsevol membre de l'escola.''' def __init__(self, nom, edat): self.nom = nom self.edat = edat print '(Inicialitzada la Persona: %s)' % self.nom def tell(self): '''Mostra els detalls.''' print 'Nom:"%s" Edat:"%s"' % (self.nom, self.edat), class Professor(Persona): ''' Representa un professor.''' def __init__(self, nom, edat, sou): Persona.__init__(self, nom, edat) self.sou = sou print '(Inicialitzat el Professor: %s)' % self.nom def tell(self): Persona.tell(self) print 'Sou: "%d"' % self.sou class Alumne(Persona): '''Representa un alumne.''' def __init__(self, nom, edat, notes): Persona.__init__(self, nom, edat) self.notes = notes print "(Inicialitzat l'Alumne: %s)" % self.nom def tell(self): Persona.tell(self) print 'Notes: "%d"' % self.notes t = Professor('Mrs. Shrividya', 40, 30000) s = Alumne('Swaroop', 22, 75) # escriu una línia en blanc print membres = [t, s] for membre in membres: membre.tell() # funciona tant per professors com per estudiants
$ python inherit.py Inicialitzada la Persona: Mrs. Shrividya) (Inicialitzat el Professor: Mrs. Shrividya) (Inicialitzada la Persona: Swaroop) (Inicialitzat l'Alumne: Swaroop) Nom:"Mrs. Shrividya" Edat:"40" Sou: "30000" Nom:"Swaroop" Edat:"22" Notes: "75"
Especifiquem els noms de les superclasses amb una tupla a continuació
del nom de la classe a la definició d'aquesta.
A continuació cridem explícitament el mètode __init__
de la superclasse fent servir la variable self
. D'aquesta
manera inicitalitzem la part de l'objecte corresponent a la superclasse.
És molt important recordar que Python no crida automàticament el constructor
de la superclasse. Cal fer-ho explícitament.
Podem veure també que podem cridar els mètodes de la superclasse
prefixant-los amb el nom d'aquesta, i passant-li
la variable self
amb la resta dels arguments.
Podem tractar les instàncies de les classes
Professor
i Alumne
com
si fossin instàncies de la classe Persona
.
Per exemple, quan fem servir el mètode tell
de la classe Persona
.
Finalment observem que el mètode tell
que es crida
és el de les subclasses i no el de la classe Persona
.
Python sempre comença a cercar els mètodes a la classe.
Si no pot trobar el mètode a la classe, llavors comença a mirar als mètodes
pertanyents a les superclasses en l'ordre especificat per la tupla de la
definició de la classe.
Una nota sobre terminologia - si apareix més d'una classe a la tupla d'herència, es diu que hi ha herència múltiple.