La programmazione orientata agli oggetti

Come definire e usare una nuova classe di oggetti.

Parlando dei dati Python ho spesso usato il termine “oggetto”. Il termine non era casuale, tutto in Python è un oggetto. Gli oggetti sono delle entità formate da dati e algoritmi strettamente collegati tra di loro. I dati dell’oggetto, che rappresentano il suo stato, sono detti attributi, gli algoritmi che permettono di far funzionare l’oggetto sono detti metodi. In generale chi usa un oggetto non deve sapere come è organizzato al suo interno, quali sono i suoi attributi e quali algoritmi lo fanno funzionare, ma deve solo conoscere la sua interfaccia, cioè i metodi che permettono all’oggetto di interagire con gli altri oggetti. Gli oggetti sono elementi di una classe: una stringa è un oggetto della classe str, una particolare lista è un oggetto della classe list, ...

Nella programmazione orientata agli oggetti, una parte del lavoro consiste nel definire le classi. Una caratteristica importante delle classi è quella di ereditare da altre classi attributi e metodi. In questo modo si possono avere intere famiglie di classi imparentate tra di loro.

Come esempio costruiremo la classe delle frazioni facendola derivare dalla classe madre di tutte le classi: object. Il primo metodo che forniremo alla classe sarà il metodo __init__(...) che viene chiamato quando si crea un nuovo oggetto di questa classe:

class Frazione(object):

  def __init__(self, num, den):
    self.num=num
    self.den=den

Alcune osservazioni:

  1. La prima riga dichiara che stiamo definendo una classe che si chiama Frazione e che discende dalla classe object.
  2. Tutta la definizione della classe deve essere indentata.
  3. Il metodo __init__ ha un primo parametro convenzionalmente indicato con self che contiene il riferimento al particolare oggetto. Poi altri due parametri i cui valori vengono associati agli attributi self.num e self.den.

Creiamo il nostro primo oggetto della classe Frazione:

>>> f1=Frazione(4, 6)
>>> print f1
<__main__.Frazione object at 0xb6e00bec>

Funziona, ma non ci dice molto... Aggiungiamo un altro metodo con un nome convenzionale: il metodo __str__ verrà chiamato tutte le volte ci sarà la necessità di convertire Frazione in stringa:

  def __str__(self):
    return "%s/%s" % (self.num, self.den)

>>> f1=Frazione(4, 6)
>>> print f1
4/6

Il risultato è già più carino! La prima operazione che ci hanno insegnato a eseguire con le frazioni è la riduzione ai minimi termini. Per farlo abbiamo però bisogno di una funzione che, dati due numeri restituisca il massimo comun divisore (abbiamo già incontrato un’implementazione di questa funzione) e modifichiamo anche il metodo __init__ in modo che dopo aver definito il numeratore e il denominatore riduca ai minimi termini la frazione:

  def __init__(self, num, den):
    self.num=num
    self.den=den
    self.riduci()

  def riduci(self):
    def macodi(a, b):
      while a<>b:
        if a>b: a-=b
        else:   b-=a
      return a
    d=macodi(self.num, self.den)
    self.num/=d
    self.den/=d

>>> f1=Frazione(4, 6)
>>> print f1
2/3

Aggiungiamo i metodi per addizionare e moltiplicare le frazioni:

  def __add__(self, altra):
    return Frazione(self.num*altra.den+self.den*altra.num,
                    self.den*altra.den)

  def __mul__(self, altra):
    return Frazione(self.num*altra.num,
                    self.den*altra.den)

>>> f1=Frazione(3, 6)
>>> f2=Frazione(4, 10)
>>> print "%s + %s = %s;  %s * %s = %s" % (f1, f2, f1+f2, f1, f2, f1*f2)
1/2 + 2/5 = 9/10;  1/2 * 2/5 = 1/5

Alcune osservazioni:

  1. All’interno del metodo riduci è definita la funzione macodi(a, b), effettivamente serve solo per ridurre ai minimi termini e quindi non è necessario che sia vista dall’esterno.
  2. I metodi __add__ e __mul__ non hanno bisogno di ridurre ai minimi termini perché questo viene fatto al momento della creazione di una nuova frazione.
  3. I metodi __add__ e __mul__ vengono chiamati quando in un’espressione deve essere eseguita l’addizione o la moltiplicazione tra due oggetti di questa classe.

Riassumendo

  • L’istruzione class permette di creare una nuova classe.
  • Una classe è formata dall’unione di dati, gli attributi e algoritmi, i metodi.
  • Un metodo particolare è __init__ che viene eseguito quando viene costruito l’oggetto.
  • Il metodo __str__ viene eseguito quando l’oggetto deve essere convertito in stringa.

Prova tu

  1. La classe precedente non funziona se uno dei termini è 0, risolvi questi casi.
  2. Aggiungi i metodi per eseguire sottrazioni e divisioni.