4.7. Metodi speciali di classe avanzati

Ci sono molti più metodi speciali dei soli __getitem__ e __setitem__. Alcuni di questi vi permettono di emulare funzionalità che non avreste mai conosciuto.

Esempio 4.17. Altri metodi speciali in UserDict

    def __repr__(self): return repr(self.data)     1
    def __cmp__(self, dict):                       2
        if isinstance(dict, UserDict):            
            return cmp(self.data, dict.data)      
        else:                                     
            return cmp(self.data, dict)           
    def __len__(self): return len(self.data)       3
    def __delitem__(self, key): del self.data[key] 4
1 __repr__ è un metodo speciale che è chiamato quando chiamate repr(istanza). La funzione repr è una funzione built-in che ritorna una rappresentazione sotto forma di stringa di un oggetto. Funziona su ogni oggetto, non solo su istanze di classi. Avete già familiarizzato con repr ed ancora non lo sapete. Nella finestra interattiva, quando scrivete solamente il nome di una variabile e premete ENTER, Python usa repr per mostrare il valore della variabile. Create un dizionario d con alcuni dati e quindi stampate repr(d) per vederlo con i vostri occhi.
2 __cmp__ è chiamato quando confrontate istanze di classe. In generale, potete confrontare ogni coppia di oggetti Python, non solamente istanze di classe, usando ==. Ci sono regole che definiscono quando tipi di dato built-in sono considerati uguali; per esempio, i dizionari sono uguali quando hanno tutte le stesse chiavi e valori, le stringhe sono uguali quando hanno la stessa lunghezza e contengono la stessa sequenza di caratteri. Per le istanze di classe, potete definire il metodo __cmp__ ed implementarne la logica di confronto, potrete quindi usare == per confrontare istanze delle vostre classi e Python chiamerà il metodo speciale __cmp__ per voi.
3 __len__ è chiamata quando chiamate len(istanza). La funzione len è una funzione built-in che ritorna la lunghezza di un oggetto. Funziona su ogni oggetto di cui si può ragionevolmente pensare di misurarne la lunghezza. La lunghezza di una stringa è il numero dei suoi caratteri; la lunghezza di un dizionario è il numero delle sue chiavi; la lunghezza di una lista o di una tupla è il numero dei suoi elementi. Per le istanze di classe, definite il metodo __len__ ed implementate il calcolo della lughezza, quindi chiamate len(istanza) e Python chiamerà il metodo speciale __len__ per voi.
4 __delitem__ è chiamato quando chiamate del istanza[chiave], che potreste ricordare come il modo per cancellare singoli elementi da un dizionario. Quando usate del su un'istanza di classe, Python chiama il metodo speciale __delitem__ per voi.
Nota
In Java, determinate quando due variabili di tipo stringa referenziano la stessa memoria fisica utilizzando str1 == str2. Questa è chiamata identità di oggetto, ed è espressa in Python come str1 is str2. Per confrontare valori di tipo stringa in Java, dovreste usare str1.equals(str2); in Python, usereste str1 == str2. Programmatori Java a cui è stato insegnato a credere che il mondo è un posto migliore in quanto == in Java confronta l'identità invece del valore potrebbero avere qualche difficoltà nell'acquisire questo nuovo “concetto” di Python.

A questo punto, potreste pensare, “tutto questo lavoro solo per fare in una classe qualcosa che potrei fare con un tipo built-in”. Ed è vero che la vita sarebbe più semplice (e l'intera classe UserDict inutile) se poteste ereditare direttamente da un tipo built-in come un dizionario. Ma anche se poteste, i metodi speciali sarebbero comunque utili perché possono essere usati in ogni classe, non solamente nelle classi wrapper come UserDict.

Metodi speciali vuol dire che ogni classe può memorizzare coppie chiave-valore come un dizionario, solo definendo il metodo __setitem__. Ogni classe può agire come una sequenza, semplicemente definendo il metodo __getitem__. Ogni classe che definisce il metodo __cmp__ può essere confrontata con ==. Se invece la vostra classe rappresenta qualcosa che ha una lunghezza, non definite il metodo GetLength, ma il metodo __len__ ed usate len(istanza).

Nota
Mentre altri linguaggi orientati agli oggetti vi lasciano definire il modello fisico di un oggetto (“questo oggetto ha il metodo GetLength”), i metodi speciali di classe Python come __len__ vi permettono di definire il modello logico di un oggetto (“questo oggetto ha una lunghezza”).

Ci sono molti altri metodi speciali. Ce n'è un intero gruppo che permette alle vostre classi di agire come numeri, permettendovi di aggiungere, sottrarre e fare altre operazioni aritmetiche sulle istanze di classe. L'esempio canonico di questo concetto è una classe che rappresenta i numeri complessi, numeri con parte reale ed immaginaria. Il metodo __call__ permette ad una classe di agire come una funzione, permettendovi di chiamare un'istanza di classe direttamente. Ci sono altri metodi speciali che permettono alle classi di avere attributi in sola lettura ed in sola scrittura; ne parleremo più diffusamente nei capitoli successivi.

Ulteriori letture