4.5. UserDict: una classe wrapper
                  
                
             
            
          
         
            
               	Come avete visto, FileInfo è una classe che
               	agisce come un dizionario. Per esplorare ulteriormente
               	questo aspetto, diamo uno sguardo alla classe
               	UserDict nel modulo UserDict, che è
               	l'antenato della nostra classe FileInfo.
               	Non è nulla di speciale; la classe è scritta in
               	Python e memorizzata in un file .py,
               	proprio come il nostro codice. In particolare, è
               	memorizzata nella directory
               	lib della vostra
               	installazione di Python.
               
            
          
            
                 | 
            
            
               | 
                  	Nella IDE di Python su Windows, potete aprire
                          rapidamente qualunque modulo nel vostro library path usando
                  -> (Ctrl-L).
                | 
            
         
         Nota storica. 
            	Nelle versioni di Python antecedenti la 2.2, non potevate
            	ereditare direttamente dai tipi built-in come stringhe,
            	liste e dizionari. Per compensare a tale mancanza, Python
            	viene rilasciato con delle classi wrapper che mimano il
            	comportamento di questi tipi built-in:
            	UserString,
            	UserList e UserDict.
            	Usando una combinazione di metodi normali e speciali, la
            	classe UserDict fa un'eccellente imitazione di un
            	dizionario, ma è semplicemente una classe come qualunque
            	altra, così potete ereditare da essa per creare delle classi
            	personalizzate che si comportano come dizionari, come abbiamo
            	fatto con FileInfo. In Python 2.2 o superiore,
            	potreste riscrivere l'esempio di questo capitolo in modo che
            	FileInfo erediti direttamente da
            	dict invece che da
            	UserDict. Comunque dovreste lo stesso leggere
            	come funziona UserDict, nel caso abbiate bisogno
            	di implementare questo genere di oggetto wrapper o nel caso
            	abbiate bisogno di supportare versioni di Python antecedenti
            	alla 2.2.
            
         
         Esempio 4.11. Definire la classe UserDict
class UserDict:                                
    def __init__(self, dict=None):             
        self.data = {}                         
        if dict is not None: self.update(dict) 
 
               
                  
                       
                      | 
                     
                        	Notate che UserDict è una classe base,
                        	non eredita da nessun'altra classe.
                        
                      | 
                  
                  
                       
                      | 
                     
                        	Questo è il metodo __init__ che andiamo a
                        	sovrascrivere
                           	nella classe FileInfo.
                        	Notate che la lista degli argomenti in questa classe
                        	antenato è diversa dai suoi discendenti. Va bene; ogni
                        	sottoclasse può avere il suo insieme di argomenti,
                        	finché chiama l'antenato con gli argomenti esatti. Qui
                        	la classe antenato ha un modo per definire i valori iniziali
                        	(passando un dizionario nell'argomento
                        	dict) di cui la nostra
                        	FileInfo non si avvantaggia.
                        
                      | 
                  
                  
                       
                      | 
                     
                        	Python supporta gli attributi  (chiamati
                        	“variabili d'istanza” in Java ed in
                        	Powerbuilder, “variabili membro” in C++),
                        	cioè dati mantenuti da una specifica istanza di una classe.
                        	In questo caso, ogni istanza di UserDict avrà un
                        	attributo data. Per referenziare questo
                        	attributo dal codice esterno alla classe, lo dovrete
                        	qualificare usando il suo nome di istanza,
                        	instance.data,
                        	nello stesso modo in cui qualificate una funzione con il nome
                        	del suo modulo. Per referenziare un attributo all'interno
                        	della classe, usiamo self come qualificatore.
                        	Per convenzione, tutti gli attributi sono inizializzati con
                        	dei valori ragionevoli nel metodo __init__. Ad ogni modo, questo
                        	non è richiesto, in quanto gli attributi, così come le
                        	variabili locali, cominciano
                           	ad esistere nel momento in cui viene loro assegnato un
                        	valore.
                        
                      | 
                  
                  
                       
                      | 
                     
                        	Il metodo update è un duplicatore di dizionario: copia tutte
                        	le chiavi ed i valori da un dizionario ad un altro. Questo
                        	metodo non cancella il dizionario di
                        	destinazione, se il dizionario di destinazione ha già alcune
                        	delle chiavi, il loro valore sarà sovrascritto, ma gli altri
                        	dati rimarranno invariati. Pensate al metodo update come ad
                        	una funzione di fusione e non di copia.
                        
                      | 
                  
                  
                       
                      | 
                     
                        	Questa è una sintassi che potreste non aver ancora
                        	visto (non l'ho usata negli esempi di questo libro). Si tratta 
                        	di una istruzione if, ma invece di avere un blocco
                        	indentato nella riga successiva, c'è semplicemente una
                        	singola istruzione sulla stessa riga, dopo i due punti.
                        	È una sintassi perfettamente legale ed è
                        	semplicemente una scorciatoia quando avete una sola istruzione
                        	in un blocco. (È come specificare una singola
                        	istruzione senza graffe in C++). Potete usare questa
                        	sintassi o potete usare il codice indentato in righe
                        	successive, ma non potete fare entrambe le cose per lo stesso
                        	blocco. 
                        
                      | 
                  
               
             
         
            
                 | 
            
            
               | 
                  	Java e Powerbuilder supportano l'overload di funzioni per
                  	lista di argomenti, cioè una classe può avere più metodi con
                  	lo stesso nome, ma diverso numero di argomenti o argomenti di
                  	tipo diverso. Altri linguaggi (principalmente PL/SQL)
                  	supportano l'overload di funzioni per nome di argomento; cioè
                  	una classe può avere più metodi con lo stesso nome e lo
                  	stesso numero di argomenti dello stesso tipo, ma i nomi degli
                  	argomenti sono diversi. Python non supporta nessuno di
                  	questi; non ha nessuna forma di overload di funzione. I metodi
                  	sono definiti solamente dal loro nome e ci può essere
                  	solamente un metodo per ogni classe con un dato nome. Così, se
                  	una classe discendente ha un metodo __init__, questo sovrascrive
                  	sempre il metodo __init__ dell'antenato,
                  	anche se il discendente lo definisce con una lista di
                  	argomenti diversa. La stessa regola si applica ad ogni altro
                  	metodo.
                  
                | 
            
         
            
                 | 
            
            
               | 
                  	Guido, l'autore originale di Python, spiega l'override dei
                  	metodi in questo modo: "Classi derivate possono sovrascrivere
                  	i metodi delle loro classi base. Siccome i metodi non hanno
                  	privilegi speciali quando chiamano altri metodi dello stesso
                  	oggetto, un metodo di una classe base che chiama un altro
                  	metodo definito nella stessa classe base, può infatti finire
                  	per chiamare un metodo di una classe derivata che lo
                  	sovrascrive. (Per i programmatori C++ tutti i metodi in
                  	Python sono effettivamente virtual.)" Se questo per voi non
                  	ha senso (confonde un sacco pure me), sentitevi liberi di
                  	ignorarlo. Penso che lo capirò più avanti.
                  
                | 
            
         
            
                 | 
            
            
               | 
                  	Assegnate sempre un valore iniziale a tutti gli attributi
                  	di un'istanza nel metodo __init__. Vi risparmierà ore di
                  	debugging più tardi, spese a tracciare tutte le eccezioni
                  	AttributeError che genera il
                  	programma perché state referenziando degli attributi non
                  	inizializzati (e quindi inesistenti).
                  
                | 
            
         
         Esempio 4.12. Metodi comuni di UserDict
    def clear(self): self.data.clear()          
    def copy(self):                             
        if self.__class__ is UserDict:          
            return UserDict(self.data)         
        import copy                             
        return copy.copy(self)                 
    def keys(self): return self.data.keys()     
    def items(self): return self.data.items()  
    def values(self): return self.data.values()
               
                  
                       
                      | 
                     
                        	clear è un normale metodo della classe; è pubblicamente
                        	disponibile per essere chiamato da chiunque in qualunque
                        	momento. Notate che clear, come tutti gli altri metodi
                        	della classe, ha self come primo argomento (ricordate,
                        	non siete voi ad
                           	includere self quando chiamate un metodo; è
                        	qualcosa che Python fa già per voi). Notate inoltre la
                        	tecnica base di questa classe wrapper: memorizza un vero
                        	dizionario (data) come attributo,
                        	definisce tutti i metodi che ha un vero dizionario e
                        	redireziona tutti questi metodi verso i loro corrispondenti
                        	del vero dizionario (nel caso lo aveste dimenticato, il
                        	metodo clear di un dizionario
                        	cancella tutte le
                           	sue chiavi ed i valori ad esse associati).
                        
                      | 
                  
                  
                       
                      | 
                     
                        	Il metodo copy di un vero dizionario restituisce un nuovo
                        	dizionario che è un esatto duplicato di quello originale
                        	(tutte le stesse coppie chiave-valore). Ma
                        	UserDict non può semplicemente redirezionare il
                        	metodo a self.data.copy, perché quel
                        	metodo ritorna un vero dizionario e noi vogliamo che ritorni
                        	un'istanza della stessa classe di self.
                        
                      | 
                  
                  
                       
                      | 
                     
                        	Usiamo l'attributo __class__ per vedere se self è uno
                        	UserDict; nel caso, siamo fortunati perché
                        	sappiamo come copiare uno UserDict: basta creare
                        	una nuova istanza di UserDict e passarle il vero
                        	dizionario che abbiamo ricavato da
                        	self.data.
                        
                      | 
                  
                  
                       
                      | 
                     
                        	Se self.__class__ non è uno
                        	UserDict, allora self deve essere una
                        	sottoclasse di UserDict (come
                        	FileInfo), nel quale caso la vita si
                        	complica un po'. UserDict non sa come
                        	fare una copia di uno dei suoi discendenti; ci potrebbero,
                        	per esempio, essere altri attributi definiti nella
                        	sottoclasse, così dovremmo iterare attraverso ognuno di
                        	essi ed essere sicuri di copiarli tutti. Fortunatamente,
                        	Python viene rilasciato con un modulo che fa esattamente
                        	questo ed è chiamato copy. Non scenderò in dettaglio
                        	(per quanto si tratti di un modulo davvero interessante,
                        	se vi interessa potete esplorarlo più a fondo da soli), è
                        	sufficiente dire che copy, può copiare oggetti arbitrari
                        	Python ed è così che noi lo usiamo.
                        
                      | 
                  
                  
                       
                      | 
                     
                        	I rimanenti metodi sono semplici, redirezionano le chiamate
                        	ai metodi built-in di self.data.
                        
                      |