Salta al contenuto principale

uno sguardo al python, parte prima.

inviato da donato taddei in privato, 05\02\2008, h. 17.14.

Questo testo è tratto dal sito italiano di python,
www.python.it
sezione documentazione/articoli
e fornisce una panoramica introduttiva  sulle caratteristiche di questo linguaggio in generale in questa prima parte e per quanto riguarda i cosiddetti
moduli nella seconda, anche in cofronto a linguaggi funzionalmente o strutturalmente affini come java o perl. 
Viene qui riportato perchè contiene quanto basta per cominciare ad esplorare, se non a decifrare e/o modificare e migliorare   il codice di nvda, che come
si sa è scritto in tale linguaggio. 
                        Donato Taddei  
Uno sguardo al Python - parte prima
   
      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... 

Ed ora, un po' di pubblicità

:

      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__
     
      >>> 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
     
      >>> open("c:\\autoexec.bat")
     
      >>> 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
     
      >>> 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 "", line 0, in ?
      NameError: re
      >>> import re
      >>> re
     
      >>> match
      Traceback (innermost last):
      File "", line 0, in ?
      NameError: match
      >>> from re import match
      >>> match
       

      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 1OperazioneRisultato
            X in SCerca X nella sequenza S
            X not in SInverso del precedente
            S + TConcatezione generica di sequeze
            S * NIterazione 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 2if :
            [elif : ]*
            [else: Comune If/then/else in Python
            while :
            [else: ]Comune While in Python - il blocco in else è eseguito
            dop la condizione se il ciclo non termina con un break
            for in :
            [else: ]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
            breakTermina un ciclo for o while
            continueEsegue la successiva iterazione di un ciclo for o while
            return []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
      ... if :
      ... break
      ... else:
      ... return None
      ...
      ... return  

      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(, [,]) 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

--------------------------------------------------------------------------------

Internal Virus Database is out-of-date.
Checked by AVG Free Edition.
Version: 7.5.516 / Virus Database: 269.6.1/776 - Release Date: 25/04/2007 12.19