A Byte of Python

Herència

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.

Ús de l'herència

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
				
				

Sortida

				
$ 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"

				
				

Com funciona

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.