A Byte of Python

Ereditarietà

Uno dei grandi benefici della programmazione orientata agli oggetti è il riutilizzo del codice, ed uno dei metodi con cui si raggiunge questo obiettivo è attraverso il meccanismo dell'ereditarietà. L'ereditarietà può essere ben immaginata come l'implementazione di una relazione di tipo e sottotipo tra classi.

Supponiamo di voler scrivere un programma che tenga traccia dei professori e degli studenti in una università. Questi possiedono alcune caratteristiche comuni come nome, età ed indirizzo. Possono anche avere delle caratteristiche specifiche come il salario, i corsi e congedi per insegnanti e matricole e le rette per gli studenti.

Si potrebbero creare due classi indipendenti per ogni tipo ed elaborarle all'occorrenza, ma aggiungere una nuova caratteristica comune significherebbe farlo su entrambe le classi indipendentemente: una soluzione che diverrebbe presto difficile da controllare.

Un metodo migliore è senz'altro quello di creare una classe comune chiamata SchoolMember ed ereditare le classi insegnanti e studenti dalla stessa, in modo che quelle ereditate siano sotto-tipi della classe comune, rendendo possibile l'aggiunta di caratteristiche specifiche senza enormi sacrifici.

Ci sono molti vantaggi usando questo approccio. Le modifiche apportate a qualsiasi funzionalità in SchoolMember si rifletteranno automaticamente nei suoi sotto-tipi. Per esempio, si può aggiungere un campo ID sia per gli insegnanti che per gli studenti semplicemente aggiungendolo alla classe SchoolMember. Le modifiche ad un sotto-tipo non influenzano altri sotto-tipi. Un vantaggio ulteriore è che si può far riferimento all' oggetto studente o insegnante come ad un oggetto di SchoolMember il che potrebbe tornare utile in alcune situazioni quali contare i membri della scuola. È questo il concetto che viene chiamato polimorfismo secondo cui un sotto-tipo può essere sostituito in ogni situazione nella quale ci si aspetta un tipo parente, ovvero tale oggetto può essere trattato proprio come un'istanza della classe parente.

C'è inoltre da osservare che si riutilizza il codice della classe parente senza la necessità di ripeterlo nelle altre classi come si dovrebbe fare nel caso si usassero classi indipendenti.

La classe SchoolMember in questa situazione viene chiamata classe base o superclasse. Le classi Teacher e Student vengono invece chiamate classi derivate o sottoclassi.

Vediamo adesso l'esempio all'opera.

Usare l'ereditarietà

Esempio 11.5. Usare l'ereditarietà

				
#!/usr/bin/python
# Filename: inherit.py

class SchoolMember:
	'''Represents any school member.'''
	def __init__(self, name, age):
		self.name = name
		self.age = age
		print '(Initialized SchoolMember: %s)' % self.name
	
	def tell(self):
		'''Tell my details.'''
		print 'Name:"%s" Age:"%s"' % (self.name, self.age),

class Teacher(SchoolMember):
	'''Represents a teacher.'''
	def __init__(self, name, age, salary):
		SchoolMember.__init__(self, name, age)
		self.salary = salary
		print '(Initialized Teacher: %s)' % self.name

	def tell(self):
		SchoolMember.tell(self)
		print 'Salary: "%d"' % self.salary

class Student(SchoolMember):
	'''Represents a student.'''
	def __init__(self, name, age, marks):
		SchoolMember.__init__(self, name, age)
		self.marks = marks
		print '(Initialized Student: %s)' % self.name
	
	def tell(self):
		SchoolMember.tell(self)
		print 'Marks: "%d"' % self.marks

t = Teacher('Mrs. Shrividya', 40, 30000)
s = Student('Swaroop', 22, 75)

print 			# stampa una linea vuota

members = [t, s]
for member in members:
	member.tell() 	# funziona sia per Teachers che per Students
				
				

Output

				
$ python inherit.py
(Initialized SchoolMember: Mrs. Shrividya)
(Initialized Teacher: Mrs. Shrividya)
(Initialized SchoolMember: Swaroop)
(Initialized Student: Swaroop)

Name:"Mrs. Shrividya" Age:"40" Salary: "30000"
Name:"Swaroop" Age:"22" Marks: "75"
				
				

Funzionamento

Per usare l'ereditarietà si specificano i nomi delle classi base in una tupla che segue il nome della classe nella sua definizione. Il metodo __init__ della classe base viene chiamato esplicitamente usando la variabile self in modo che si possa inizializzare la parte della classe base dell'oggetto. Questo è importante da ricordare; Python non chiama automaticamente il costruttore della classe base, occorre farlo esplicitamente.

Un'altra cosa da notare è che si possono chiamare i metodi della classe base facendoli precedere dal nome della classe per poi passarli alla variabile self insieme ad altri argomenti.

Da notare che che quando si usa il metodo tell della classe SchoolMember, si possono trattare istanze di Teacher o Student come istanze di SchoolMember.

Inoltre si osservi che viene chiamato il metodo tell del sotto-tipo e non il metodo tell della classe SchoolMember. Per comprendere questo punto ricordiamo che Python parte sempre cercando i metodi nel tipo, come fa in questo caso. Se non trova il metodo, prosegue la ricerca nelle proprie classi base, una per una e nell'ordine specificato nella tupla nella definizione della classe.

Una nota sulla terminologia: se viene listata più di una classe nella tupla che definisce l'ereditarietà, si parla di ereditarietà multipla.