Download Documentazione FAQ Aiutaci!!!

Vai alla seconda parte dell'articolo

Vai all'indice degli articoli


Esaminiamo il Python, un linguaggio di scripting object oriented, molto chiaro nella sua sintassi, semplice ma potente che per le sue molteplici virtù conquista ogni giorno maggiori consensi...


Uno sguardo al Python

di Michele Sciabarrà


Java è un linguaggio che è venuto alla ribalta molto di recente, quando i giochi sui linguaggi di programmazione sembravano fatti: chi avrebbe mai immaginato, solo pochi anni fa, che si sarebbe potuto affermare in così breve tempo un nuovo major programming language per il quale sono già disponibili decine con ambienti di svilupp e numerose librerie? Allo stesso modo, Python si è imposto quando sembrava non esserci spazio per nuovi linguaggi di scripting: tra Perl, JavaScript, Tcl/Tk e Scheme non sembrava esserci spazio per altri… In realtà nel mondo informatico ci sarà sempre spazio per l'innovazione, fermo restando il fatto che i vecchi sistemi non spariscono mai del tutto… vedi COBOL e FORTRAN.

Le similitudini tra Python e Java non finiscono qui: il bello di Java, a parte l'aspetto "politico" di rappresentare una piattaforma alternativa a Windows, è dato dal linguaggio stesso: Java sembra riassumere per certi versi il meglio del C++ e del Visual Basic. Java è moderno, object oriented senza compromessi ed esagerazioni; è più potente del Visual Basic, da cui eredita la capacità di creare intefacce grafiche con semplicità; si presenta ben più semplice del C++, da cui deriva molte potenzialità, espresse però in maniera più semplificata e chiara sia sintatticamente che semanticamente.

La storia del Python sembra molto simile nell'ambito dei linguaggi di scripting in ambiente Unix. Perl e Tcl/Tk sono stati per anni l'unica opzione; entrambi sono derivati dallo scripting di shell, ed entrambi con pregi e difetti (sintassi e semantica astrusa il Perl, alcune limitazioni di scalabità il Tcl/Tk). Python sembra riassumere per certi versi il meglio del Perl e del Tcl/Tk. Python è moderno, object oriented senza compromessi ed esagerazioni; è più potente del Tcl/Tk, da cui eredita la capacità di creare intefacce grafiche con semplicità; si presenta ben più semplice del Perl, da cui deriva molte potenzialità espresse però in maniera più semplificata e chiara sia sintatticamente e semanticamente. Non è un caso che l'ultima frase sia analoga dalla precedente sostituendo Perl a C++, Tcl/Tk a Visual Basic e Python a Java…

In effetti la mia esperienza personale con il Python assomiglia molto alla mia esperienza con il Java. Non ho mai voluto usare il Visual Basic, preferendovi per anni il C++, e scontrandomi regolarmente con le sue astrusità, finché ho scoperto Java: la versione più semplice del C++ che offre molto dei vantaggi del Visual Basic… e da allora ho usato quasi esclusivamente Java. Analogamente non ho mai voluto usare il Tcl/Tk, preferendovi per anni il Perl, e scontrandomi regolarmente con le sue astrusità finchè non ho scoperto il Python…


Primo impatto

Python è un interprete. Niente di strano trattandosi di un linguaggio di scripting. A differenza di altri però può essere utilizzato interattivamente. Eseguendo python dalla riga di comando (che sia Unix o NT) otteniamo il prompt (in questo caso su NT):


PythonWin 1.5.1 (#0, Apr 13 1998, 20:22:04) [MSC 32 bit (Intel)] on win32

Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam

>>>


Dopo il >>> possiamo digitare dei comandi e quindi eseguire delle semplici operazioni:


>>> 2+2

4

>>> a=5

>>> a+1

6

>>> def sum(a,b): return a+b

>>> sum(4,5)

9


Come si vede, il Python è innanzitutto un valutatore di espressioni, ma consente di assegnare variabili e definire delle funzioni. Fin qui niente di eccezionale. Ora però cominciamo a fare qualcosa un po' più audace.


>>> __builtins__

<module '__builtin__'>

>>> dir(__builtins__)

['ArithmeticError', … OMISSIS …

'abs', 'apply', 'callable', 'chr', 'cmp', 'coerce', 'compile', 'complex', 'delattr', 'dir', 'divmod', 'eval', 'execfile', 'filter', 'float', 'getattr', 'globals', 'hasattr', 'hash', 'hex', 'id', 'input', 'int', 'intern', 'isinstance', 'issubclass', 'len', 'list', 'locals', 'long', 'map', 'max', 'min', 'oct', 'open', 'ord', 'pow', 'range', 'raw_input', 'reduce', 'reload', 'repr', 'round', 'setattr', 'slice', 'str', 'tuple', 'type', 'vars', 'xrange']


La variabile __builtins__ è di tipo modulo, come dice il messaggio ottenuto dalla sua valutazione; si tratta del modulo che contiene tutti i builtins, ovvero le funzioni predefinite del sistema. Per vedere il contenuto di un modulo (o di un qualsiasi namespace in generale - un modulo è un particolare namespace) posso usare il builtin dir, che in questo caso mi consente di elencare tutti gli altri builtin. I builtin sono funzioni immediatamente disponibili senza particolari operazioni. Continuiamo le nostre esplorazioni, provando ad utilizzare un builtin.


>>> open

<built-in function open>

>>> open("c:\\autoexec.bat")

<open file 'c:\autoexec.bat', mode 'r' at 870920>

>>> x=open("c:\\autoexec.bat")

>>> dir(x)

['close', 'closed', 'fileno', 'flush', 'isatty', 'mode', 'name', 'read', 'readinto', 'readline', 'readlines', 'seek', 'softspace', 'tell', 'truncate', 'write', 'writelines']

>>> x.readline

<built-in method readline of file object at 8745d0>

>>> x.readline()

'rem - By Windows 95 Network - C:\\WINDOWS\\net start\012'


Vediamo se vi è chiaro quello che ho fatto: open è un builtin; infatti valutando open mi viene comunicato che si tratta di una funzione builtin (un oggetto quindi di tipo diverso dal modulo visto prima); questo oggetto può essere usato invocandolo con un parametro, ovvero il nome di un file: così facendo ottengo un altro oggetto che rappresenta un file aperto. Per utilizzare il file lo salvo in una variabile e provo ad esaminarne il contenuto con dir, scoprendo che l'oggetto contiene al suo interno una serie di funzioni (o più correttamente metodi). Provo ad utilizzare la readline, che è una funzione e non una proprietà e quindi invoco la funzione, senza parametri, ottenendo quello che mi aspetto: la prima riga del file AUTOEXEC.BAT che ho aperto.

I builtin sono immediatamente disponibili, ma si tratta di un caso particolare: in generale le funzioni è necessario importarle. Come è possibile accedere ai metodi di un oggetto, è anche possibile accedere alle funzioni presenti in un file, o meglio modulo. Per usare le funzioni di un modulo, occorre importarlo. Vediamo subito come si fa, esaminando il modulo per le espessioni regolari (un assoluto must per chi vuole utilizzare il Python come un sostituto per il Perl).


>>> re

Traceback (innermost last):

File "<interactive input>", line 0, in ?

NameError: re

>>> import re

>>> re

<module 're'>

>>> match

Traceback (innermost last):

File "<interactive input>", line 0, in ?

NameError: match

>>> from re import match

>>> match

<function match at 850e90>


Non ci interessa in questa sede il funzionamento del modulo re, per il momento ci limitiamo ad esaminanare il meccanismo di importazione. Inizialmente re non è disponibile, lo diventa dopo che eseguiamo import re. A questo punto possiamo vedere il modulo, ma le funzioni in esso contenute (che in questo caso non sono metodi) sono accessivibili solo con la sintassi puntata: re.match. È però possibile rendere accessibile definitivamente una funzione senza dove specificare il modulo con la sintassi from re import match. Notare che in questo modo viene resa disponibile per l'accesso immediato soltanto una funzione, anche se possono essere importate tutte (ma in generale, a meno di moduli semplici, non è il caso).


Sequenze e dizionari

In Python abbiamo tre categorie di tipi di dato. I tipi primitivi (interi e float), gli oggetti e le sequenze. Le sequenze presenti in Python attualmente sono stringhe, liste e tuple. Vediamo innanzitutto le stringhe


>>> s = 'Hello'

>>> s = s+', world'

>>> s

'Hello, world'

>>> len(s)

12

>>> s[0:5]

'Hello'

>>> s[7:]

'world'

>>> s[0]

'H'

>>> s[-1]

'd'

>>> dir(s)

[]

>>> x='a'

>>> x=x+'b'

>>> x

'ab'


Le stringhe possono essere espresse con una costante: basta scrivere i caratteri che la compongono tra virgolette, singole o doppie. Le variabili in Python, come ormai dovrebbe essere chiaro, non sono tipizzate e non vanno dichiarate: quindi per creare una variabile contentente una stringa basta assegnare una costante stringa alla variabile. Le stringhe sono immutabili: questo vuol dire che una volta create non possono essere modificate, si può solo creare un'altra stringa. Le stringhe possono essere concatenate utilizzando l'operatore +, come si vede nell'esempio, ma in questo caso vengono buttate via le due stringhe preesistenti, costruendone una nuova risultante dalla concatenazione delle due precedenti. Le stringhe, come tutte le sequenze in genere, sono indicizzabili tramite l'operatore [], che può specificare un range, separato dai due punti: questa operazione viene chiamata slicing.

Riferiamoci all'esempio di prima. Possiamo notare subito che i range partono da 0 e che il range s[a:b] indica tutti gli elementi da a incluso a b escluso. Usare un numero negativo come indice equivale ad indicizzare a partire dall'ultimo elemento; quando ometto il primo elemento del range si intende 0, quando ometto il secondo si intende il len della sequenza. Un esame della stringhe con dir rivela che non ha metodi: questo significa che una stringa può essere manipolata soltanto utilizzando i builtin e gli operatori predefiniti. Un'altra sequenza immutabile è la tupla:


>>> x = 1,2,3

>>> x

(1, 2, 3)

>>> a,b,c = x

>>> a

1

>>> b

2

>>> c

3

>>> x[0:1]

(1,2)

>>> x,y = 7,8

>>> x+y

15


Separando con una virgola una serie di elementi si costruisce una tupla, o meglio si impaccano (packing) gli elementi un una tupla. Questa operazione ha il suo constrario nello spacchettamento (unpacking) della tupla, che si ottiene ponendo una serie di variabili a sinistra di un assegnamento. Essendo le tuple sequenze, anche su di esse si può operare con lo slicing, come le stringhe. Python ha dunque l'assegnamento multiplo, che tecnicamente è un packing ed unpacking di tupla. Le tuple sono comode in molti casi, ma sono immutabili come le stringe, e si può operare con esse solo utilizzando poche operazioni, quelle standard per le sequenze, riassunte in Tabella 1. Esiste una struttura dati molto più flessibile, ma ovviamente anche più inefficiente, ovvero la lista:


>>> x=[1,3]

>>> x

[1, 3]

>>> x.append(4)

>>> x

[1, 3, 4]

>>> x.insert(1,2)

>>> x

[1, 2, 3, 4]

>>> x.reverse()

>>> x

[4, 3, 2, 1]

>>> dir(x)

['append', 'count', 'index', 'insert', 'remove', 'reverse', 'sort']


Tabella 1

Operazione

Risultato

X in S

Cerca X nella sequenza S

X not in S

Inverso del precedente

S + T

Concatezione generica di sequeze

S * N

Iterazione di S per N volte

S[I]

I-simo elemento di S

S[I:J]

Slice da I incluso a J escluso.

len(S)

Lungheszza della sequenza

Min(S), Max(S)

Minino e massimo di S

A differenza delle tuple, le liste sono mutabili e si possono eseguire più operazioni su di esse. Le liste possono essere create utilizzando l'apposita sintassi, comprendo gli elementi tra parentesi quadre, e su di esse si può operare utilizzando una serie di metodi predefiniti: append, insert, reverse, eccetera, come si può vedere nell'esempio, oltre al solito slicing. Vediamo infine i dizionari, ovvero gli array associativi che sono anche essi tipi primitivi in Python:


>>> x={'a':1, 'b':2}

>>> x

{'b': 2, 'a': 1}

>>> x['a']

1

>>> x['c']=3

>>> dir(x)

['clear', 'copy', 'get', 'has_key', 'items', 'keys', 'update', 'values']

>>> x.keys()

['b', 'c', 'a']

>>> x.values()

[2, 3, 1]

>>> x.items()

[('b', 2), ('c', 3), ('a', 1)]


Il funzionamento dovrebbe essere abbastaza ovvio. Le costanti per definire i dizionari sono della forma { key:val, keys:val, … } . Per accedere agli elementi si usa la sintassi per l'indicizzazione di una sequenza; si può assegnare un elemento per crearlo, e si può eliminare un elemento con del. I dizionari non sono sequenze, ma li tratto in questo paragrafo per affinità con le altre strutture dati primitive. Se serve si possono ottenere facilmente le sequenze delle chiavi, dei valori e di tutti gli elementi (una lista di tuple).


Controllo di flusso

Python è un linguaggio imperativo e ha quindi il controllo di flusso usuale, con if, while e for. Un aspetto importante e caratteristico del Python è la modalità di "accorpamento" dei comandi, ovvero come si definiscono i blocchi. I linguaggi tradizionali usano le parentesi: le graffe C e Java, begin-end il Pascal ecceter. Il Python usa l'indentazione per raggruppare gli statementi. Vediamo subito un esempio:


>>> def max(a,b):

... if a>b:

... return a

... else:

... return b

...

>>> max(1,2)

2


Tabella 2

if <condition>: <block>
[elif <condition>: <block>]*
[else: <block>

Comune If/then/else in Python

while <condition>: <block>
[else: <block>]

Comune While in Python - il blocco in else è eseguito dop la condizione se il ciclo non termina con un break

for <target> in <condition-list>: <block>
[else: <block>]

Itera per ogni elemento della sequenza e lo assegna alla variabile target - il blocco in else è eseguito dopo la condizione se il ciclo non termina con un break

break

Termina un ciclo for o while

continue

Esegue la successiva iterazione di un ciclo for o while

return [<result>]

Ritorna il risultato dalla funzione o metodo corrente - se result è omesso ritorna None

I comando if, while, for, def sono composti, quindi devono essere seguite da un blocco, che viene separato dalla condizione con due punti. Il blocco comincia dalla riga successiva e finisce in una indentata quanto la corrente o meno. Visivamente si ha quello che ci si aspetta: tutte le linee seguenti ad un comando composto e più indentate di esso appartengono al blocco di quel comando. Oltre alla sintassi già di per sé abbastanza chiara, questa caratteristica è una marcia in più che contribuisce a rendere i sorgenti in Python estremamente leggibili: quella che prima era solo una norma estetica in Python diventa una regola di programmazione. Come dire che un programma è corretto solo se è anche scritto ordinato! Inoltre, l'uso dell'indentazione elimina molti problemi abbastanza frequenti legati al matching delle parentesi. Infine questo contribuisce a rendere la sintassi minimale, quello che si vuole da un linguaggio di scripting comodo e veloce da usare. Gli elementi del controllo di flusso sono riassunti in tabella 2. Esaminiamoli uno ad uno.

L'if è il consueto blocco condizionale. L'unica particolarità da notare è che, poichè gli if in cascata risulterebbero sconvenienti da scrivere, per esempio:


>>> if a=1:

... …

... else:

... if a=2:

... …

... else:

... if a=3:

... …

... else:


esiste la clausola elif, per cui l'esempio di sopra può essere scritto più semplicemente e chiaramente in Python come:


>>> if a=1:

... …

... elif a=2:

... …

... elif a=3

... …

... else:

... …


Il ciclo while è simile all'if eccetto che itera il corpo finchè la condizione non è verificata. La particolarità da notare in questo caso è una comoda clausola else che viene eseguita solo quando la condizione diventa falsa. Sono disponibili i comandi break per interrompere il ciclo e continue per continuare il ciclo alla iterazione successiva. Notare che l'else del while (e anche del for che vedremo dopo) non viene eseguito se il ciclo termina con un break. Questo rende agevole scrivere certi cicli in cui si deve distinguere se il ciclo è stato terminato normalmente oppure no. Per esempio, supponiamo di voler cercare una riga in un file per fare dei calcoli, e ritornare il valore speciale None se la riga non è stata trovate nel file. Ecco come si può fare in Python:


>>> while <ci sono righe nel file>

... if <trovata la riga>:

... break

... else:

... return None

... <elabora la riga>

... return <la riga elaborata>


Infine il for, che è speciale, nel senso che non è un while compattato come in Java o C. Il for itera su tutti gli elementi di una sequenza:


>>> for i in 'abc':

... print i, "-",

...

a - b - c -

>>> for i in 1,2,3:

... print i*i

...

1

4

9


Per inciso notiamo la print, che prende i suoi argomenti separati da virgole; se l'ultimo argomento è omesso (notare la virgola senza niente dopo) la print non genera un newline, come fa nel secondo caso. Il for può essere usato sulle stringhe, sulle tuple e anche sulle liste. In Python quindi non c'è un modo esplicito per andare da 6 a 12 a passi di 2, ma occorre appoggiarsi alla funzione range(<inizio>, <fine> [,<passo>]) che costruisce una lista con gli elementi necessari. Quindi questo è il modo corretto per andare da 6 a 12 a passi di due:


>>> for i in range(6,13,2):

... print i,

...

6 8 10 12


Notare che abbiamo usato 13 perchè in un range il limite superiore (12) è escluso.

Python supporta la programmazione ad oggetti in maniera consistente ed elegante, ed ha una libreria di classi piuttosto ampia che copre molte necessità. La dichiarazione delle classi in Python è piuttosto complessa, per cui per motivi di spazio non approfondiremo qui l'argomento ma lo rimandiamo ad un articolo successivo.


Windows e Java

Python è nato originariamente in ambiente Unix, come supporto per un progetto in cui i linguaggi esistenti non erano adeguati, e si è diffuso quando l'autore (Guido Van Rossum) ha reso disponibile il linguaggio in sorgente su UseNet (le News di Internet). Però il Python non è legato ad Unix in maniera così forte come il Perl, e infatti il linguaggio si trova a suo agio senza scomporsi anche in altri ambienti. In particolare sono disponibili due interessanti implementazioni: il PythonWin per Windows e il JPython per Java. Il PythonWin è innanzitutto il porting del Python in ambiente Windows, corredato di una sorta di IDE e di librerie specifiche per Windows. Si può vedere in Figura 1 uno snapshot ottenuto con il PyhonWin che dimostra come esso consenta di accedere ad una buona parte della API di Win32. PythonWin ha numerose altre armi al suo arco, tra cui l'accesso all'OLE Scripting: di conseguenza si può usare Python per le ASP ma anche per scriptare Word o Excel.

Figura 1

Il Python può anche essere utilizzato per scriptare la Java Platform: esiste infatti una reimplementazione in Java, chiamata JPython. In questo contesto l'interprete Python tradizionale, che è scritto in C, viene chiamato CPython, e il JPython è la versione dell'interprete in Java. La cosa interessante è la compatibilità tra le versioni: infatti il JPython e il CPython usano le stesse librerie! Una cosa interessante è che il JPython consente di avere accesso con assoluta semplicità alle librerie Java e può essere quindi utilizzato per sperimentare interattivamente con Java. Per esempio ecco un listatino che crea una finesta con un bottone digitato direttamenete al prompt del JPython:


>>> import java

>>> from java import awt

>>> f = awt.Frame("Demo")

>>> b = awt.Button("Ok")

>>> l = awt.Label("Hello")

>>> f.add("Center", l)

>>> f.add("South", b)

>>> f.setSize(100,100)

>>> f.setVisible(true)

>>> f.setVisible(1)


Conclusioni

Non concluderò l'articolo dicendo che vale la pena di provarlo perchè arricchisce la vostra cultura. Io il Python lo uso, per progetti reali e in contesti commerciali. Mi limiterò a raccontare come ho cominciato ad usarlo. Devo dire che quando ho dato una buona occhiata al Python ero molto scettico ma anche abbastanza frustrato: avevo perso alcune ore per risolvere un baco in uno script Perl in teoria da cinque minuti, dovuto al fatto che non avevo afferrato bene un dettaglio sintattico. Più ci pensavo più mi sembrava assurdo che dopo quattro anni che lavoravo con il Perl, ancora non ne avevo digerito completamente la sintassi. Quella sintassi così stramba per me è un impiccio che costringe ad avere sempre il manuale sottomano (non mandatemi email dicendomi che non siete d'accordo, vi prego!). Perché nessuno aveva ancora inventato un linguaggio di scripting chiaro e pulito come Java?

Mi serviva un linguaggio di scripting (Java non è un linguaggio di scripting) innanzitutto portabile tra Unix e NT, che avesse la stessa plasticità del Perl, con strutture dati primitive potenti come tabelle hash e liste. Il linguaggio che cercavo doveva gestire bene le stringhe, con espressioni regolari comprese nel linguaggio, e doveva essere interpretabile senza alcuna ricompilazione in modo da poter scrivere uno script ed eseguirlo anche con solo l'editor (o meglio ancora con il cat >file o il copy con: file). Come il Perl il linguaggio doveva essere poco tipizzato per non perdere tempo a fare dichiarazioni e progettazione per semplici script (per esempio in Java solo per impostare un pattern tipo *.txt o un event-handler occorre dichiarare una classe!). Questo fantomatico linguaggio doveva avere una sintassi più pulita e un orientatamento agli oggetti decisamente più netto e meno arzigogolato del Perl. E se fosse dotato anche di una shell con la quale interagire (mancanza del Perl - per farlo si deve lanciare il debugger!) non sarebbe stato male. Infine, possibilmente si doveva trattare di linguaggio che avesse qualche supporto, ovvero non il solito linguaggetto geniale tirato fuori da qualche ricercatore che non usa nessuno. Avevo sentito parlare del Python e del fatto che era abbastanza usato, ma mi ci volevano un paio di giorni per studiarlo. Quando finalmente ho trovato il tempo e ho avuto modo di approfondire meglio il linguaggio, ho capito di aver trovato quello che cercavo. E a giudicare dal supporto che circola su Internet, a quanto pare non sono il solo.


Bibliografia

[1] http://www.python.org - " Python Software Activity", il sito principale del Python dove si può trovare di tutto relativo al Python

[2] Guido Van Rossum "Programming Python", O'Reilly 1997, il libro ufficiale sul Python scritto dall'autore del linguaggio.


Michele Sciabarrà, laureato in Scienze dell'Informazione, è direttore tecnico di SATORI Network Solutions, ditta marchigiana focalizzata su Java e le tecnologie di Internet. Programma in svariati linguaggi da 15 anni, e in Java da quando il linguaggio era in versione alpha. È stato tra gli espositori di applicazioni Java già alla 1st Italian Java Conference, ha scritto decine di articoli su Java e sviluppato numerose applicazioni in Java. Mantiene sul Web un corso online di Java in italiano (http://www.satorins.com/CorsoJava)


Vai alla seconda parte dell'articolo

Vai all'indice degli articoli