L'integrazione può
essere realizzata soprattutto in due modi: usando Python
per scrivere il programma principale che utilizzi componenti
scritte in Java; oppure integrando nel programma Java
un interprete che esegua codice Python dall'interno
dell'applicazione. Le due soluzioni hanno naturalmente
ambiti d'uso e scopi differenti.
Java
dentro Python
Come accennato
nell'introduzione, il Python può essere utilizzato
per fungere da "collante" fra varie componenti
scritte in un linguaggio di sistema come Java. Si può
quindi sviluppare un modello in Java, sfruttando tutti
gli strumenti messi a disposizione dal linguaggio per
ottenere strutture efficaci e robuste, e poi usarle
all'interno di un programma principale scritto in Python.
Questo approccio è utile per esempio per sviluppare
rapidamente prototipi, interfacce utente, test, ecc.
Jython permette oltretutto
di importare le librerie Java ed utilizzarle (quasi)
come librerie native. Come esempio vediamo un semplice
programma che visualizza un'interfaccia Swing con due
bottoni.
from
pawt.swing import JFrame, JPanel, JButton
from java.lang import System
def
hello(e):
print "Ciao, mondo!"
def
exit(e):
System.exit(0)
frame
= JFrame('Esempio', visible=1)
panel = JPanel()
frame.contentPane.add(panel)
helloButton
= JButton('Ciao', actionPerformed=hello)
panel.add(helloButton)
exitButton
= JButton('Esci', actionPerformed=exit)
panel.add(exitButton)
frame.pack()
Se il programma è stato salvato nel file hello.py,
per eseguirlo basterà invocare l'interprete:
jython
hello.py
Come si può notare
la sintassi del Python è molto chiara e sintetica
ed è possibile
realizzare applicazioni utilizzando librerie Java e
scrivendo poche righe di codice. In alcuni casi viene
fornito da Jython un modulo wrapper intorno a particolari
librerie; ad esempio
pawt trova l'ubicazione delle librerie Swing che, a
seconda della distribuzione Java, possono trovarsi in
package differenti come
java.awt.swing, javax.swing, ecc.
Python è un linguaggio
orientato agli oggetti, per cui gli sviluppatori Java
si troveranno a loro agio potendo utilizzare un paradigma
di programmazione a loro familiare. Rivediamo l'esempio
precedente aggiungendo una classe.
from
pawt.swing import JFrame, JPanel, JButton
from java.lang import System
class
Counter(object):
def __init__(self):
self.count = 0
def hello(self, e):
self.count += 1
print "Ciao, mondo! (%d)" % (self.count,)
def exit(self, e):
print "Hai salutato %d volte." % (self.count,)
System.exit(0)
class Hello(JFrame):
def __init__(self, counter, title='Esempio'):
JFrame.__init__(self, title, visible=1)
self.counter = counter
def run(self):
panel = JPanel()
self.contentPane.add(panel)
helloButton = JButton('Ciao', actionPerformed=self.counter.hello)
panel.add(helloButton)
exitButton = JButton('Esci', actionPerformed=self.counter.exit)
panel.add(exitButton)
self.pack()
c = Counter()
hello
= Hello(c)
hello.run()
In quest'ultimo esempio abbiamo definito due classi,
una addirittura ereditata da una classe Java!
Python dentro Java
Un'altra soluzione
consiste nell'inglobare (in gergo embed) l'interprete
nella nostra applicazione Java, così che possa
eseguire
codice Python dall'interno. Questo approccio permette,
per esempio, di implementare per le nostre applicazioni
un'architettura a plugin che preveda una parte centrale
in Java che svolga le elaborazioni principali e delle
parti periferiche che estendano le funzionalità
dell'applicazione in modo autonomo. La parte centrale
(o framework) fornisce dei punti di inserimento per
le parti periferiche (plugin) e
comunica con esse. I plugin ricevono informazioni dal
framework e le elaborano in maniera autonoma ed indipendente.
Una simile architettura
permette di estendere rapidamente le funzionalità
dell'applicazione, senza modificarne in alcun modo la
struttura o conoscerne i dettagli interni, e permette
una maggiore flessibilità nella scelta delle
funzionalità da distribuire, per
esempio in installazioni diverse.
Benché i plugin
possano essere scritti in Java, la scelta di un linguaggio
di scripting come il Python ha alcuni vantaggi:
Vediamo un semplice
esempio di embedding. Simuliamo un sistema Java che
acquisisce la temperatura di una vasca d'acqua attraverso
un sensore. Vogliamo che sia possibile estendere questo
sistema con uno script Python in modo che si possano
eseguire delle azioni ogni volta che viene acquisita
la temperatura, per esempio una scritta d'avvertimento
quando la temperatura dell'acqua scende sotto i 37°.
import
java.util.Timer;
import java.util.TimerTask;
import org.python.util.PythonInterpreter;
public
class Vasca extends TimerTask {
private
final PythonInterpreter interpreter;
public
Vasca() {
interpreter
= new PythonInterpreter();
}
public
void run() {
Double
val = new Double(((Math.random() * 5) + 1) + 35);
interpreter.set("t",
val);
interpreter.execfile("script.py");
}
public
static void main(String[] args) {
Vasca
vasca = new Vasca();
Timer
timer = new Timer();
timer.schedule(vasca,
0, 1000);
}
}
Il simulatore non fa
altro che emettere un numero casuale ogni secondo; inoltre
contiene un interprete Python a cui viene passato il
numero generato e quindi eseguito uno script presente
sul filesystem:
interpreter.set("t", val);
interpreter.execfile("script.py");
Il metodo set() passa
all'interprete l'oggetto val con il nome t: da quel
momento l'ambiente Python conterrà una variabile
t con il numero generato. Il metodo execfile() invece
esegue nell'ambiente Python il codice contenuto nel
file script.py; il file, per essere correttamente localizzato,
dovrà trovarsi nel classpath. Lo script può
essere qualcosa di simile a questo:
#
avverti se la temperatura scende sotto i 37°
if
t<37:
print "temperatura: %2.2f°" % (t,)
print "attenzione!"
Dopo una prima riga
di commento, lo script verifica che la temperatura
non scenda sotto 37°, altrimenti stampa la temperatura
e
l'avvertimento.
Conclusioni
La potenza e la
flessibilità dei linguaggi di scripting sono
spesso sottovaluate, così come spesso non sono
ben comprese le differenze con i linguaggi di sistemi.
L'introduzione di linguaggi di scripting nei processi
di sviluppo può portare numerosi benefici, in
termini di qualità del codice, chiarezza e tempi
di sviluppo. Inoltre è notevole la duttilità
di questi strumenti, che come abbiamo visto possono
interfacciarsi ai linguaggi di sistema con modalità
diverse a seconda delle esigenze che si presentano.