|
Abbiamo visto i moduli, ovvero file che contengono definizioni di
funzioni e altri comandi. Creando un nuovo file, chiamato per esempio
modulo.py, contenente la definizione di funzione f(),
è possibile importarlo con import modulo per poi chiamare
la funzione f usando la sintassi modulo.f(). Per inciso,
il file è importabile se la directory che lo contiene si trova
nel PYTHONPATH. L'impostazione del PYTHONPATH varia da sistema a
sistema, ma generalmente si tratta di impostare la variabile di
ambiente PYTHONPATH. Notare che f in questo caso è una
funzione, non un metodo o altro, anche se per accedervi occorre
utilizzare il prefisso modulo. Un modulo è infatti un
esempio di namespace; gli elementi in un namespace sono detti
attributi. Per accedere ad un elemento di un namespace si
utilizza la sintassi namespace.attributo. I builtin (per
esempio dir) si trovano nel namespace __builtins__., e
sono accedibili (come caso particolare) anche se non si specifica un
prefisso. Un altro modo per evitare il prefisso è utilizzare
from <namespace> import <attribute>, ma è
meglio non abusare di questa facility per non avere problemi di
collisioni di nomi.
Le istanze delle classi sono anche essi dei namespace, anche se si
comportano in maniera più sofisticata dei moduli. Si tratta in un
certo senso della naturale evoluzione della programmazione modulare
verso la programmazione ad oggetti. Nel resto dell'articolo si
assumono basi di OOP: non spiegheremo che cosa è una classe, una
istanza, un costruttore o una funzione virtuale, per cui se siete a
digiuni di queste nozioni potreste incontrare qualche difficoltà
nella lettura.
Abbiamo visto che in Python ci sono svariati tipi di dato, come le
sequenze, i dizionari, e i moduli. Adesso introdurremo un nuovo tipo
di dato, ovvero la classe. Prima vediamo come si usano le
classi esistenti, poi esamineremo come si definiscono nuove
classi. Sfruttiamo l'aspetto interattivo del Python, che ci consente
di imparare cose nuove sperimentando con l'interprete a riga di
comando. Come esperimento per imparare l'uso di oggetti preleveremo ed
esamineremo una pagina Web con una connessione http, usando la classe
HTTP del modulo httplib:
>>> import httplib
>>> httplib.HTTP
<class httplib.HTTP at 874be0>
>>> h
<httplib.HTTP instance at 875930>
Innanzitutto notiamo che le classi, come le funzioni, sono
contenute dentro moduli. In particolare la classe HTTP è
contenuta nel modulo httplib, per cui occorre importare il
modulo (altrimenti tale classe non è accessibile). Valutando
httplib.HTTP osserviamo che si tratta di un oggetto di tipo
classe, utilizzabile però in maniera analoga ad una
funzione. Chiamando la classe, otteniamo un oggetto di tipo
istanza della classe, come si vede nell'esempio. In pratica la
classe è una funzione costruttore che produce
istanze. L'oggetto istanza così costruito si comporta in
maniera simile ad un modulo, ovvero come contenitore di funzioni che
possono essere chiamate. In realtà un oggetto è qualcosa di
più di un modulo, in quanto mantiene uno stato separato per ogni
istanza. Quindi la classe è la naturale evoluzione del concetto
di modulo: un oggetto è un namespace analogamente ad un
modulo. La differenza principale è che si possono creare istanze
diverse dello stesso modulo, ottenendo namespace separati con
variabili indipendenti. Questo è il punto cruciale. I dati
contenuti in una istanza di una classe vengono inizializzati chiamando
una particolare funzione di inizializzazione:
__init__. L'ultimo elemento importante che differenzia le
classi dai moduli è il fatto che su di esse si può applicare
l'ereditarietà, come vedremo più avanti. Riassumendo:
-
Namespace: spazio di nomi, contenente
attribuiti; agli attributi di un namespace si accede con la
sintassi namespace.attributo.
-
Modulo: namespace, che contiene funzioni e
altri elementi, letto da un file.
-
Classe: una funzione in grado di generare
istanze, ovvero un costruttore.
-
Istanza: namespace che contiene funzioni dette
metodi; l'istanza differisce dal modulo per il fatto che
viene creata dinamicamente (mentre i moduli sono definiti con dei
file) e che gli attributi sono indipendenti per ogni istanza, anche
se l'istanza viene creata dallo stesso costruttore.
-
Metodo: funzione appartenente ad una
classe, che solitamente modifica le variabili dell'istanza a cui
appartiene (in altre parole cambia lo stato dell'oggetto).
Torniamo al nostro esempio, e utilizzando l'istanza di HTTP
chiamandone i metodi:
>>> h.putrequest('GET', '/')
>>> h.endheaders()
>>> h.getreply()
(200, 'OK', <mimetools.Message instance 876bc0>
>>> f = h.getfile()
>>> lines = f.readlines()
>>> lines[0]
<html>
Di ogni oggetto, per usarlo occorre conoscerne il
funzionamento leggendone la documentazione. Nel caso che stiamo
esaminando adesso, una istanza di HTTP invia delle richieste
corrispondenti ai comandi HTTP. Questa classe in realtà non
maschera molto il protocollo http, in quanto occorre
conoscerlo almeno per sommi capi. In particolare, occorre sapere
che la richiesta per prelevare una pagina web è GET,
seguita dall'URL della pagina senza l'indirizzo dell'host. Dopo la
richiesta, occorre anche aggiungere delle informazioni
supplementari che vengono date al Web Server utilizzando degli
header. Nel nostro caso non inviamo alcuna informazione
supplementare, completando la richiesta con endheader(), e
leggiamo la risposta del Web Server. Poiché è OK (codice
200, stringa di messaggio OK, altre informazioni nella
istanza mimetools.Message) possiamo leggere la pagina web
tramite un oggetto file per la lettura. Per semplicità,
leggiamo le righe ponendole in una lista, e ne stampiamo solo la
prima.
|