Python 3 - Classi

Creazione di istanze

  • Per create un'istanza di una classe possiamo usare il nome della classe come fosse una funzione:
    class test:
    ....
    x = test()
  • Questo crea una nuova istanza che contiene una copia del namespace della classe
  • Questo namespace contiene inizialmente tutti i metodi e gli attributi definiti nella classe
  • Possiamo aggiungere attributi e metodi all'istanza in ogni momento
  • Questi attributi e metodi saranno della sola istanza.

Attributi

  • Come già per le variabili, gli attributi vengono definiti nel momento in cui gli si assegna un valore
  • Gli attributi definiti all'interno della classe e fuori dai metodi sono nel contesto della classe
  • Naturalmente verranno duplicati nel namespace delle istanze, quini ne avremo sempre due copie indipendenti: una <classe>.<attributo> statica ed una self.<attributo> associata all'istanza
  • Se invece all'interno di un metodo definiamo un attributo come self.<attributo>, esso sarà esclusivamente associato all'istanza

Attributi

  • Per quanto riguarda gli attributi statici usati nella loro copia di istanza, occorre porre attenzione ai tipi mutabili
  • Se noi abbiamo un attributo statico che si riferisce, ad esempio, una lista, l'attributo avrà una copia di istanza, che però si riferirà alla stessa lista
  • Se in questo caso modifichiamo la lista tramite un metodo di lista (quindi non ne sostituiamo il riferimento), la modifica avrà effetto globale.

Costruttore

  • Anche in Python è previsto un costruttore ha il nome standard __init__(self[,<parametro>]...)
  • self è il parametro obbligatorio che si riferirà all'istanza
  • È possibile indicare parametri per il costruttore, eventualmente anche con dei valori di default
  • Non è invece possibile definire più costruttori con parametri diversi, in quanto il secondo costruttore sovrascriverebbe il primo e ne avremmo comunque uno solo

Costruttore

  • La gestione della memoria in Python è automatica, quindi non c'è necessità di distruggere un'istanza, per recuperare memoria
  • Nel caso comunque sia necessario effettuare qualche operazione, come chiudere un file prima che un'istanza venga eliminata dalla memoria, esiste il metodo __del__(self)

Metodi

  • I metodi vengono definiti come normali funzioni, ma all'interno della classe.
  • I metodi della classe devono avere tutti almeno un parametro (il primo), generalmente chiamato self, che si riferisce all'istanza su cui vengono chiamati
  • Anche se il metodo non utilizza self, occorre indicarlo perché Python lo passa ad ogni metodo invocato su di un'istanza
  • Per accedere ad ogni attributo o metodo dell'istanza siamo obbligati a passare attraverso self.

Metodi

  • Se creiamo nella classe un metodo privo del parametro self, non potremo invocarlo su di un'istanza e lo potremmo considerare come un metodo statico
  • È sempre possibile invocare un metodo tramite la classe, aggiungendo esplicitamente il primo parametro con l'istanza.

Definizioni di classi

  • Per definire una classe usiamo la parola chiave class:
    class test:
  • La classe è inclusa in un blocco, quindi dovremo utilizzare un livello di indentazione
  • La definizione della classe implica la definizione di un nuovo namespace
  • Gli attributi globali ed i metodi verranno inseriti in questo namespace
  • Sia attributi che metodi sono pubblici. Non possiamo fare altrimenti

Definizioni di classi

  • Nel namespace finiscono solo i nomi dei metodi, quindi non è possibile sovraccaricare i metodi

Namespaces

  • Prima di addentrarci nelle classi, meglio dare un'occhiata al concetto di namespace ed al suo uso in Python
  • Un namespace è una collezione di nomi, associati ai relativi oggetti (inteso in senso lato - funzioni, variabili, ecc.)
  • Un nome può comparire in più namespace. Si riferirà a oggetti diversi
  • Se uso un nome di variabile o funzione, mi riferisco al default namespace che contiene le variabili e le funzioni definite nel mio programma ed una serie di variabili e funzioni di sistema

Namespaces

  • Se carico un modulo, come abbiamo fatto con random o simplegui, esso si porta dietro un suo namespace
  • È per questo che quando usiamo i metodi di random o di simplegui dobbiamo sempre indicare il nome del modulo prima, come in random.randrange()

Esercizio

  • Modificare l'esercizio del cielo stellato:
    1. creare una classe stella, che contenga le coordinate, una variabile periodo ed una momento
    2. la stella avrà un costruttore che riceverà la x., la y e il periodo
    3. la stella avrà un metodo di disegno, che riceverà il pannello
    4. ad ogni ridisegno, la stella incrementerà il "momento" fino a raggiungere periodo
    5. la stella crescerà da 1 a cinque pixel per la prima parte del periodo, per poi calare di nuovo fino alla fine del periodo stesso

Ereditarietà

  • In Python è implementata l'ereditarietà, anche quella multipla
  • Viene implementata clonando ed unendo i namespace delle classi base
  • Per indicare le superclassi basta metterle tra parentesi, dopo il nome della classe:
    class figlio(padre, madre):
  • Visti i ragionamenti precedenti, in questo modo viene garantita l'ereditarietà e la possibilità di ridefinire metodi

Ereditarietà

  • Per chiamare metodi della superclasse, dobbiamo farlo in maniera esplicita, tramite la classe:
    class figlio(padre):
    def metodo(self,x):
    padre.metodo(self,x)
  • Per verificare il tipo di una istanza possiamo utilizzare is:
    class c:
    pass
    v = c()
    print type(v) is c
  • Ma questo non ci dice se la nostra istanza discende da una certa classe.

Ereditarietà

  • Se vogliamo verificare la discendenza, useremo isinstance(<istanza>,<classe>):
    class padre:
    pass
    class figlio(padre):
    pass
    v = figlio()
    print isinstance(v, padre)

Secondo Esercizio

  • Modificare l'esercizio della pallina e racchetta:
    1. creare una classe oggetto che contenga le coordinate X ed Y nonché larghezza ed altezza
    2. oggetto implementerà un metodo scontro che riceve come parametro un altro oggetto
    3. implementerà anche un metodo disegno, che riceve il panel come parametro
    4. creare una classe palla derivata da oggetto che visualizza la pallina
    5. creare una classe racchetta che implementa la racchetta
    6. riscrivere il metodo di disegno utilizzando le nuove classi

Sovrapposizione degli operatori

  • In Python è implementata una rudimentale forma di sovrapposizione degli operatori
  • Quanto noi eseguiamo operazioni tra istanze di classe vengono invocati dei metodi particolari, chiamati magic methods
  • Sono facilmente riconoscibili perché il loro nome è sempre racchiuso tra __ e __, come __init__ e __del__
  • Ad esempio, se vogliamo implementare i confronti tra classi possiamo implementare il metodo __cmp__(self,other), che ritornerà un valore intero come strcmp()

Sovrapposizione degli operatori

  • Esistono altre magic funzion per implementare i singoli tipi di confronto, se necessario
  • È anche possibile ridefinire le operazioni matematiche con __add__ __sub__ __mul__ __div__ ecc...
  • È possibile anche emulare una lista o implementare un rudimentale sistema di incapsulamento degli attributi, tramite le magic function
  • Documentazione sulle magic function si può trovare in questo PDF: http://www.rafekettler.com/magicmethods.pdf
[any material that should appear in print but not on the slide]