Python + Java = Jython !
Note: Return to tutorial view.
Introduzione
Un linguaggio - tanti interpreti
Il linguaggio Python ha vari interpreti.
Il più noto è CPython. E' scritto in ansi C e portato con successo su svariate architetture (Windows, Linux/Unix, Mac OS X, OS/2, Amiga ecc.).
Esistono però anche altre implementazioni dell'interprete:
Jython che gira sulla macchina virtuale java.
IronPython che gira sul framework .NET
In questo articolo riporterò la mia esperienza con Jython.
Perchè utilizzare Jython
Ci sono due diversi punti di vista:
- per i "pythonisti" (come me) Jython permette di utilizzare e estendere classi scritte in java. E' possibile quindi accedere a svariate librerie di codice java che svolgono delle funzioni che (questo è il mio caso) non sono disponibili altrimenti.
- Per i "Javisti" invece è possibile utilizzare Jython per scrivere codice più snello, leggibile e di facile manutenzione. Questo grazie alle caratteristiche proprie del linguaggio, a questo proposito consiglio di fare riferimento a questo link dove vengono esplorate alcune differenze tra i linguaggi.
Jython attualmente implementa la versione 2.3 di Python (quindi mancano yield, classi nuovo stile e decoratori). Ciò nonostante il porting di codice non è troppo difficoltoso.
Per ottenere buoni risultati è necessaria una discreta conoscenza delle classi Java (oltre che di Python).
Per dimostrare quanto scritto ecco un esempio di codice Java:
import javax.swing.JFrame;
import javax.swing.JButton;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class MyExample
implements ActionListener {
public void actionPerformed(ActionEvent e) {
System.out.println("Ouch!");
}
public static void main(String[] args) {
JFrame frame = new JFrame("My Frame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300,300);
JButton button = new JButton("Push Me!");
frame.getContentPane().add(button);
ActionListener listener = new MyExample();
button.addActionListener(listener);
frame.setVisible(true);
}
} L'esempio è elementare: si tratta di un frame con un bottone. Se viene premuto il bottone viene stampato "Ouch!"
import javax.swing as swing
def printMessage(event):
print "Ouch!"
if __name__== "__main__":
frame=swing.JFrame(title="My Frame", size=(300,300))
frame.defaultCloseOperation=swing.JFrame.EXIT_ON_CLOSE;
button=swing.JButton("Push Me!", actionPerformed=printMessage)
frame.contentPane.add(button)
frame.visible=1 Il relativo esempio in Python è molto più stringato e leggibile.
Un esempio concreto
Il linguaggio xslt è un linguaggio (per la precisione un dialetto di xml) progettato per trasformare un file xml in un altro file xml (nel nostro caso un file fo)
Il linguaggio fo (formatting object) contiene un layout (più o meno come un file odt o doc)
Questo è un esempio tratto dalla "vita reale".
Avevo la necessità di scrivere un programma di elaborazione di file xsl-fo. In pratica dovevo trasformare un file xml in un file fo (attraverso un foglio di stile xsl) e infine ottenere un pdf.
Purtroppo non ho trovato una libreria per effettuare questa operazione a parte apache.fop. Questo libreria è scritta in Java e quindi ho deciso di portare il mio progetto in Jython e utilizzarla direttamente.
Per prima cosa ho scaricato il file jar di apache fop.
Ho creato un batch per aggiungere tutti jar necessari alla classpath (path dove vengono ricercate le librerie di Java).
Seguendo l'esempio sul sito ho "tradotto" la chiamata a funzione da Java a Jython.
Come prima cosa faccio l'import dei packages Java e dei moduli Python
from javax.xml.transform import Transformer,TransformerFactory,Source,Result from javax.xml.transform.stream import StreamSource from javax.xml.transform.sax import SAXResult import os.path
L'equivalente sintassi Java non mi avrebbe permesso l'import di più moduli su più righe. Inoltre i namespaces di Python permettono una maggiore leggibilità.
Di seguito creo un paio di oggetti al caricamento dei moduli:
fopFactory = FopFactory.newInstance() foUserAgent = fopFactory.newFOUserAgent() factory = TransformerFactory.newInstance()
Questi oggetti sono riutilizzabili per tutte le trasformazioni che dovrò effettuare.
E' interessante notare un altro "risparmio" che mi permette Python: non sono obbligato a dichiarare il tipo restituito! In Java infatti sono obbligato a scrivere:
FopFactory fopFactory = FopFactory.newInstance();
Ora creo una funzione che restituisce un oggetto transformer necessario alla trasformazione da xml a fo. Viene preso come argomento il nome del file xsl.
def loadxslt(xsltfile):
curdir="file:///" + os.path.dirname(xsltfile).replace("\\","/") + "/"
foUserAgent.setBaseURL(curdir)
trans=factory.newTransformer(StreamSource(xsltfile))
trans.setParameter("versionParam", "2.0")
return(trans) Il trasformer ottenuto si passa alla funzione che, dato in input il pathname dell'xml di origine e del pdf di output, si occupa della trasformazione e del rendering in pdf:
def xml2pdf(src,transformer,pdffile): out = java.io.BufferedOutputStream(java.io.FileOutputStream(pdffile)) try: fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, out) res = SAXResult(fop.getDefaultHandler()) transformer.transform(src, res) finally: out.close()
Il risultato finale è questo:
from javax.xml.transform import Transformer,TransformerFactory,Source,Result
from javax.xml.transform.stream import StreamSource
from javax.xml.transform.sax import SAXResult
import os.path
fopFactory = FopFactory.newInstance()
foUserAgent = fopFactory.newFOUserAgent()
factory = TransformerFactory.newInstance()
def loadxslt(xsltfile):
curdir="file:///" + os.path.dirname(xsltfile).replace("\\","/") + "/"
foUserAgent.setBaseURL(curdir)
trans=factory.newTransformer(StreamSource(xsltfile))
trans.setParameter("versionParam", "2.0")
return(trans)
def xml2pdf(src,transformer,pdffile):
out = java.io.BufferedOutputStream(java.io.FileOutputStream(pdffile))
try:
fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, out)
res = SAXResult(fop.getDefaultHandler())
transformer.transform(src, res)
finally:
out.close()
if __name__=="__main__":
import sys
#sintassi nomecomando file.xml file.xsl file.pdf
transf=loadxslt(sys.argv[2])
xml2pdf(sys.argv[1],transf,sys.argv[3])
Conclusioni
Jython è un grande strumento a disposizione di chi lavora in Java e anche di chi preferisce Python ma è "obbligato" a dover lavorare con Java :-)
