IntroducaoJythonRodrigo

Introdução ao Jython

O que é Jython?

Jython é a união entre o Java e o Python, devemos aqui fazer uma separação entre a linguagem de programação Java e a plataforma Java.

Primeiro temos a linguagem (um C++ que não deu certo) que possui certas características. Podemos citar por exemplo a orientação a objetos e o fato de Java não ser compilada de forma a gerar um binário nativo (características em comum com o Python). É ai que entra a plataforma Java, todos que conhecem o Java sabem que a propaganda do Java é "Write Once, Run Anywhere", algo como compile uma vez rode em qualquer lugar. Java funciona assim porque ele não roda diretamente em contato com a plataforma nativa e sim em cima de uma máquina virtual que cria uma camada com API's padronizadas. Bom se é assim eu preciso usar "aquela linguagem " para programar para Java?

As vantagens do Jython

Pondo a mão na massa

Instalação

Aqui nós cobriremos apenas a instalação do pacote binário, mas como o Jython é distribuido seguindo uma licença bem liberal você pode compilar ele do fonte. Não é necessário, já que a plataforma Java é unificada.

Download

Em http://www.jython.org/download.html você encontrará as versões mais recentes do Jython.

Baixe uma versão atualizada da VM do Java (dhuu!!). Jython funciona a partir da versão 1.1. Nos meus testes ele não rodou em nada mais velho que a versão 1.2. Estou utilizando o 1.4 no meu Linux mas os programas rodam igualmente bem em um 1.3 (instalado em OS/2).

A instalação é simples. Simplesmente execute o .class:

java jython-????

e siga as instruções da interface gráfica de instalação.

Obs: não inclua a extensão do arquivo no comando acima, se por exemplo o arquivo baixado é jython-21.class, o comando será java jython-21 -- RodrigoVieira

O primeiro programa em Python no Java

Vamos fazer um Hello world! rapidinho:

Execute o Jython (normalmente existe um script pra isso no diretório onde você instala o Jython).

Jython 2.1 on java1.4.1 (JIT: null)
Type "copyright", "credits" or "license" for more information.

>>>from javax.swing import *
>>>
>>> janela = JFrame('Teste')
>>> label = JLabel('Ola mundo!')
>>> janela.getContentPane().add(label)
>>> janela.setBounds(20,20,200,200)
>>> janela.setVisible(1)

E temos o nosso Hello world!.

Trabalhando com classes "Java Puro-Sangue"

Primeiro vamos criar uma classe em Java. Vamos utilizar um painel com um rótulo:

   1 /**
   2  * Painel com uma caixa de texto e um label.
   3  * 
   4  * Um objeto muito usado
   5  * @version 0.0.1
   6  */
   7 
   8 import java.awt.*;
   9 import javax.swing.*;
  10 
  11 public class lentry extends JComponent {
  12     private JLabel label;
  13     private JTextField texto;
  14 
  15     public lentry(String rotulo) {
  16         label = new JLabel(rotulo);
  17         texto = new JTextField();
  18         setLayout(new GridLayout(2,2));
  19         add(label);
  20         add(texto);
  21     }
  22 
  23     public String getText() {
  24         return this.texto.getText();
  25     }
  26 
  27     public void setText(String te) {
  28         this.texto.setText(te);
  29     }
  30 }

Bom o próximo passo é compilar o arquivo:

javac lentry.java

Com o .class em mãos vamos utilizá-lo no Jython. Aqui vai um exemplo simples:

   1 from javax.swing import *
   2 from java import awt
   3 
   4 # importando a nossa classe Java
   5 import lentry
   6 
   7 class teste(JFrame):
   8    def __init__(self, rotulo='Teste'):
   9       JFrame.__init__(self, rotulo)
  10       teste = lentry('Rotulo')
  11       self.setLayout(awt.BorderLayout())
  12       self.getContentPane().add(teste)
  13       self.setBounds(20,20,200,300) # define o tamanho da janela
  14       self.setVisible(1)
  15 
  16 if __name__=="__main__":
  17    janela = teste()

Aqui vemos algumas diferenças básicas entre o Python e o Java. Basicamente não importa como foi feito o objeto o efeito final para uso dele é o mesmo (aquilo que os pythonistas já estão acostumados mas no quesito importação nós vamos ver a diferença: Java trata cada arquivo como uma classe, já para Python um arquivo pode conter várias classes. Também não é necessário que a classe tenha o mesmo nome do arquivo). Por isso quando vamos importar algo escrito em Java (the lang) a importação é feita do modo simples:

import nome_do_objeto

Para todos os outros efeitos nem mesmo o programador será capaz de dizer se está usando um módulo "Puro-Sangue" ou um módulo feito em Python.

Observação: A maneira como o Python trata os arquivos em relação aos objetos é o principal motivo pelo qual os seus programas feitos no Jython não são totalmente independentes do Jython uma vez compilados as classes criadas neles são primariamente objetos do Python, embora possam ser usados no Java.

Utilizando o Jython no Java

Este exemplo veio do site do Jython, me foi enviado por alguem da lista, não me lembro quem e infelizmente eu perdi o e-mail. Primeiro vamos criar a seguinte classe em Python:

   1 #------------------------------------
   2 # WelcomeFrame.py
   3 #-----------------------------------
   4 
   5 import javax.swing as swing
   6 import java.awt as awt
   7 import java.lang as lang
   8 
   9 def exit(event):
  10    lang.System.exit(0)
  11 
  12 class WelcomeFrame(swing.JFrame):
  13    names = ["Groucho", "Chico", "Harpo"]
  14    quotes = {"Groucho": "Say the secret word",
  15              "Chico": "Viaduct?", "Harpo": "HONK!"}
  16 
  17    def __init__(self, title="Welcome to Jython", size=(200, 200)):
  18       """@sig public WelcomeFrame(String title,
  19                                 java.awt.Dimension size)"""
  20       self.title = title
  21       self.windowClosing = exit
  22       self.contentPane.layout = awt.FlowLayout()
  23       self.field = swing.JTextField(preferredSize=(200,20))
  24       self.contentPane.add(self.field)
  25       buttons = [self.createButton(each) for each in self.names]
  26       for eachButton in buttons:
  27          self.contentPane.add(eachButton)
  28       self.pack()
  29 
  30    def buttonPressed(self, event):
  31       self.field.text = self.quotes[event.source.text]
  32 
  33    def createButton(self, name):
  34       "@sig public javax.swing.JButton createButton(String name)"
  35       return swing.JButton(name, preferredSize=(100,20),
  36                            actionPerformed=self.buttonPressed)
  37 
  38    def addQuote(self, marxBrother, quote):
  39       "@sig public void addQuote(String marxBrother, String quote)"
  40       self.quotes[marxBrother] = quote
  41 
  42 if __name__ == '__main__':
  43    WelcomeFrame().show()

Feito isso passamos ao passo 2: Compilar o código acima.

Para isso utilizamos o jythonc, que faz parte do pacote do Jython:

# jythonc WelcomeFrame.py

As outras opções do compilador do Jython serão discutidas em um capítulo mais a frente, você também pode ler mais sobre ele na documentação do Jython. Por enquanto isso deve bastar, o Jythonc cria um subdiretório na pasta corrente chamado jpywork, dentro dele encontraremos as classes que usaremos, são duas, uma é a classe NomedaClasse.class e a outra NomedaClasse$_PyInner.class.

A primeira contém um proxy Java para o objeto Python, e a segunda contém o objeto Python propriamente dito. É bom lembrar que como o Python trata os objetos de maneira distinta do Java, não é possível o acesso direto do Java aos objetos criados em Python, embora o acesso do Python aos objetos do Java seja direto.

Obviamente você não precisa obrigatoriamente do $_PyInner.class, pode-se usar no lugar dele o código Python puro ou a versão bytecompiled dele. Depois de copiar as duas classes pro diretório onde vamos trabalhar criaremos um objeto em Java que extende o objeto que criamos no Python:

   1 // ------------------------------------
   2 // FrameUser.java
   3 // ------------------------------------
   4 
   5 import javax.swing.JButton;
   6 
   7 
   8 public class FrameUser {
   9    public static void main(String[] argv) {
  10       WelcomeFrame f = new WelcomeFrame("From Java");
  11       JButton newButton = f.createButton("Zeppo");
  12       f.addQuote("Zeppo", "Hello");
  13       f.getContentPane().add(newButton);
  14       f.pack();
  15       f.show();
  16    }
  17 }

Em seguida vamos compilar o código acima. Mas antes disso temos que configurar o CLASSPATH

Eu uso o Java 1.4.1 que normalmente não usa CLASSPATH para acessar suas própias bibliotecas somente para acessar bibliotecas que não estejam no diretorio $JAVA_HOME/jre/lib/ext. Eu não utilizo as bibliotecas do Jython neste diretório, se você utiliza pode pular este passo.

# configura no Linux (Shell Bash)
$ export CLASSPATH=LugarOndeEstaOJython/jython.jar:.

O passo seguinte é compilar o fonte:

$ javac FrameUser.java

Uma vez compilado é só rodar o programa, com o CLASSPATH acima configurado ele roda sem problemas.

Acessando banco de dados com o Jython

O Jython é bastante democrático em relação ao acesso à bancos de dados. Pode-se utilizar o acesso do Java, para quem está mais acostumado ao Java. E quem está passando do PythonC ao Jython? Utiliza-se o zxJDBC, ele cria um objeto com interface semelhante à do Python para acesso a banco de dados.

Eu fiz testes com o PostgreSQL e o MySQL, em ambos os casos o acesso foi perfeito. Aqui vai um pequeno exemplo de como fazer o acesso com o zxJDBC. Para para maiores informações leia a documentação que vem junto com o Jython (ela é bastante completa e não é nosso interesse aprofundar aqui}.

   1 from com.ziclix.python.sql import zxJDBC
   2 
   3 #-----------------------------------------------
   4 # d->URI to database
   5 # u->user
   6 # p->password
   7 # v->java class driver
   8 
   9 d, u, p, v = "jdbc:mysql://localhost/mysql", "rodrigo",
  10              None, "com.mysql.jdbc.Driver"
  11 
  12 # do python connect
  13 db = zxJDBC.connect(d,u,p,v)
  14 
  15 cursor = db.cursor()
  16 cursor.execute("show tables")
  17 ret = cursor.fetchone()
  18 print(ret[0])
  19 db.close()

Extensões do Python ao Java

O Python cria uma série de extensões ao Java. Primeiro as do próprio PythonC, já que a grande maioria delas funciona bem no Jython. Quem não conhece bem estas extensões eu recomendo primeiro uma visita ao PythonC.

Nós trataremos aqui de dois exemplos:

O segundo item é importante já que o método de eventos de Java é bastante deficiente (ele trabalha com um listener centralizado onde o programador deve checar os eventos e objetos e depois executar a chamada apropriada).

Obs.: PAWT significa Python AWT e é a versão Python para a biblioteca AWT do Java. Essa biblioteca contêm os pacotes de Layout e eventos do Python além de "atalhos" para outras coisas de maior uso no dia a dia. Para usar Swing você deve importá-la como pawt.swing).

Configurando layout no Jython usando o Pawt.GridBag

Uma das maneiras mais usadas para se configurar layout no Java manualmente é o GridBag. Ele permite que se estipulem tamanhos diversos para widgets baseando-se na proporção da tela, o que permite conservar a aparência da janela não importando o tamanho da mesma. O único problema do GridBag é que ele é um pouco complicado e não muito intuitivo de se usar. Um exemplo do uso de GridBag pode ser visto no seguinte código retirado do manual do Java:

   1 import java.awt.*;
   2 import java.util.*;
   3 import java.applet.Applet;
   4 
   5 public class GridBagEx1 extends Applet {
   6 
   7    protected void makebutton(String name,
   8                              GridBagLayout gridbag,
   9                              GridBagConstraints c) {
  10       Button button = new Button(name);
  11       gridbag.setConstraints(button, c);
  12       add(button);
  13    }
  14 
  15    public void init() {
  16       GridBagLayout gridbag = new GridBagLayout();
  17       GridBagConstraints c = new GridBagConstraints();
  18 
  19       setFont(new Font("Helvetica", Font.PLAIN, 14));
  20       setLayout(gridbag);
  21 
  22       c.fill = GridBagConstraints.BOTH;
  23       c.weightx = 1.0;
  24       makebutton("Button1", gridbag, c);
  25       makebutton("Button2", gridbag, c);
  26       makebutton("Button3", gridbag, c);
  27 
  28       c.gridwidth = GridBagConstraints.REMAINDER; //end row
  29       makebutton("Button4", gridbag, c);
  30 
  31       c.weightx = 0.0;                  //reset to the default
  32       makebutton("Button5", gridbag, c); //another row
  33 
  34       c.gridwidth = GridBagConstraints.RELATIVE; //next-to-last in row
  35       makebutton("Button6", gridbag, c);
  36 
  37       c.gridwidth = GridBagConstraints.REMAINDER; //end row
  38       makebutton("Button7", gridbag, c);
  39 
  40       c.gridwidth = 1;                //reset to the default
  41       c.gridheight = 2;
  42       c.weighty = 1.0;
  43       makebutton("Button8", gridbag, c);
  44 
  45       c.weighty = 0.0;                  //reset to the default
  46       c.gridwidth = GridBagConstraints.REMAINDER; //end row
  47       c.gridheight = 1;               //reset to the default
  48       makebutton("Button9", gridbag, c);
  49       makebutton("Button10", gridbag, c);
  50 
  51       setSize(300, 100);
  52    }
  53 
  54    public static void main(String args[]) {
  55       Frame f = new Frame("GridBag Layout Example");
  56       GridBagEx1 ex1 = new GridBagEx1();
  57 
  58       ex1.init();
  59 
  60       f.add("Center", ex1);
  61       f.pack();
  62       f.setSize(f.getPreferredSize());
  63       f.show();
  64    }
  65 }

O mesmo código em Python ficaria assim:

   1 from java.awt import *
   2 from pawt import GridBag
   3 #layouts mais simples nem precisam disto
   4 from java.awt import GridBagConstraints as GB
   5 from java.lang.System import exit
   6 
   7 class janela(Frame):
   8    def __init__(self):
   9       self.bag = GridBag(self)
  10 
  11          # Criacao dos botoes
  12          botao1 = Button('Button1', actionPerformed=self.sair)
  13          botao2 = Button('Button2', actionPerformed=self.sair)
  14          botao3 = Button('Button3', actionPerformed=self.sair)
  15          botao4 = Button('Button4', actionPerformed=self.sair)
  16          botao5 = Button('Button5', actionPerformed=self.sair)
  17          botao6 = Button('Button6', actionPerformed=self.sair)
  18          botao7 = Button('Button7', actionPerformed=self.sair)
  19          botao8 = Button('Button8', actionPerformed=self.sair)
  20          botao9 = Button('Button9', actionPerformed=self.sair)
  21          botao10 = Button('Button10', actionPerformed=self.sair)
  22 
  23          # Definicao do layout
  24          self.bag.add(botao1, weightx=0.0, weighty=0.0)
  25          self.bag.add(botao2, weightx=0.0, weighty=0.0)
  26          self.bag.add(botao3, weightx=0.0, weighty=0.0)
  27          self.bag.addRow(botao4, weightx=0.0, weighty=0.0)
  28          self.bag.addRow(botao5, fill='HORIZONTAL')
  29          self.bag.add(botao6, gridwidth=GB.RELATIVE, fill='HORIZONTAL')
  30          self.bag.addRow(botao7)
  31          self.bag.add(botao8, fill='BOTH', gridheight=2)
  32          self.bag.addRow(botao9, fill='HORIZONTAL')
  33          self.bag.addRow(botao10, fill='HORIZONTAL')
  34          #
  35          self.setBounds(200,200,280,170)
  36 
  37       def sair(self,evt):
  38          exit(0)
  39 
  40 if __name__=="__main__":
  41    janela().show()

Não é simplesmente uma questão de simplicidade do código. O metódo também é bem mais intuitivo que o do Java. Para a maioria dos programas você não precisará nem dos GridBagConstraints que eu importei aqui.

Os métodos padrão são:

Trabalhando com eventos

Em Java você trabalha com eventos da seguinte forma (o código está em Python):

   1 class action(awt.event.ActionListener):
   2    def actionPerformed(self,event):
   3       java.lang.System.exit(0)
   4 
   5 button = awt.Button("Close Me!")
   6 button.addActionListener(action())

Quem programa em Java sabe o quanto essa implementação pode tornar-se infinitamente complexa. A meu ver a maneira de tratar os eventos constitui a maior falha do Java. Bom, e o Python?

Em Python nós escreveriamos assim:

   1 def exit(event):
   2     java.lang.System.exit(0)
   3 
   4 button = awt.Button("Close Me!", actionPerformed=exit)

Existem alguns eventos controlados pelo Python. O mais utilizado é o nosso já conhecido actionPerformed. Outra forma de configurar eventos é:

   1 obj.keyPressed = funcao
   2  
   3 def funcao(event):
   4     pass

Uma lista completa dos eventos mapeáveis desta forma pode ser conseguida num manual Java :), já que praticamente todos os eventos do Java podem ser mapeados desta forma. O código a seguir mostra como mapear as teclas pressionadas em uma caixa de texto:

   1 from javax.swing import *
   2 from java import awt
   3 
   4 def alert(panel,message):
   5    result = JOptionPane.showMessageDialog(panel,message);
   6 
   7 class KeyChek(JFrame):
   8    def __init__(self):
   9       self.setBounds(200,200,200,70)
  10       self.getContentPane().setLayout(awt.GridLayout(2,1))
  11       self.label = JLabel('Digite algo:')
  12       self.texto = JTextField()
  13       self.panel = self.getContentPane()
  14       self.panel.add(self.label)
  15       self.panel.add(self.texto)
  16       self.texto.keyPressed = self.textoKeyPressed
  17 
  18    def textoKeyPressed(self, evt):
  19       tecla = evt.getKeyChar()
  20       alert(self.panel, 'Voce pressionou %c' % (tecla))
  21 
  22 if __name__=="__main__":
  23    KeyChek().show()

Compilando Fontes para Classes Java reais

Este texto foi extraído e traduzido na íntegra do Manual do Jython.

Na sessão sobre uso do Jython em Java nós vimos como criar .class com o jythonc. Lá nós conseguimos utilizar as classes geradas no Java, mas ela ainda conservava as dependências do próprio Jython e do seu codigo fonte original em Python.

Isto é necessário para escrever Servlets, Beans e Applets. Existem duas ferramentas importantes para isto e elas vem junto com o Jython. A primeira que nós já conhecemos é o jythonc. O jythonc gera código real Java e depois compila este código para criar classes Java "reais".

Abaixo temos uma tabela com as opções do jythonc:

--package package | -p package | -package package

Grava o código resultante no pacote definido.

--jar jarfile | -j jarfile | -jar jarfile

Grava todo o resultado do "congelamento" dentro de um jar. Implica na opção --deep.

--deep | -d | -deep

Compila todas as dependências (ou pelo menos tenta) do seu programa junto com ele.

--core | -c | -core

Inclue o Jython (+- 130K) dentro do seu programa. Você precisa disto para escrever applets. Implica na opção --deep.

--all | -a | -all

Inclue todo o Jython dentro do seu programa.

--bean jarfile | -b jarfile | -bean jarfile

Gera um arquivo jar com um manifest para Bean.

--addpackages pkgs | -A pkgs | -addpackages pkgs

Obtém a lista de dependências do Java destes pacotes. O default é org.python.modules e org.apache.oro.text.regex.

--workdir directory | -w directory | -workdir directory

Especifica o diretório de trabalho, o padrão é ./jpywork.

--skip modules | -s modules | -skip modules

Não inclui nenhum destes módulos no pacote final.

--compiler path | -C path | -compiler path

Usa um compilador Java alternativo, se setado para None não gera código Java. Pode-se também setar python.jythonc.compiler no registry.

--compileropts options | -J options

Seta as opções para serem passadas para o compilador. Você também pode setar em python.jythonc.compileropts no registry.

--falsenames names | -f names | -falsenames names

Uma lista (separada por virgulas) de nomes que serão sempre falsos.

--help | -h

Imprime uma mensagem de ajuda.

[module]*

Uma lista de módulos para o "congelamento". Podem ser tanto os módulos que estejam no python.path ou arquivos .py.

Para poder criar classes Java reais o módulo Python deve conter uma classe com o mesmo nome do módulo e esta classe deve estender uma classe ou interface Java. A classe gerada será uma subclasse daquilo que você estendeu.

Compilado métodos Python para métodos Java

Normalmente métodos Python compilados não são acessíveis do Java. Simplesmente não existe informação suficiente a respeito dos métodos Python para escrever um método Java compatível. Existem duas maneiras de fazer isto:

   1 def setExpression(self, e):
   2    "@sig public void setExpression(java.lang.String e)"

Exemplos

Para compilar os applets que vem de demonstração no Jython em uma máquina com Windows:

c:\jython\Demo\applet> jythonc --core --deep --jar appletdemo.jar *.py

Para criar um esqueleto que permita uma classe Python ser usada como um java.awt.Component no Java:

/usr/local/jython-2.1/demo$ jythonc Graph.py

Para gerar um bean:

/usr/local/jython-2.1/Demo/bean$ jythonc --deep --bean f2c.jar \
                                 conversion.FahrenheitToCelsius

Freezing modules

Aqui temos a segunda ferramenta. Na verdade ela é executada pelo próprio jythonc, e consiste no "congelamento do módulo". A aplicação congelada pode ser tratada como qualquer aplicação feita em Java.

Existem algumas diferenças entre uma aplicação Jython e uma "congelada":

Uma comparação entre o Python/Java/PythonC

Aqui vamos fazer uma comparação entre estas linguagens. A linguagem C será colocada como parâmetro, afinal é a origem de todas as outras e para a maioria dos analistas (eu inclusive) o padrão maxí. Os dados de desempenho foram obtidos a partir de testes feitos. Os programas de testes continham a seguinte estrutura:

Ling.

Port.*

Bib.**

Expansão***

Desempenho

Aprendizado

Veloc. desenv.

C/C++

++++++++

++++++++

++++++++

++++++++

+

+++

Java

++++

+++++

++++

+++

+

++

Jython

+++

++++++

+++

++

++++++

+++++++

PythonC

+++++

++++

+++++++

+++++

++++++++

++++++++

Dados:

Segundo os dados do Jython newspaper o Java é 1,7 vezes mais lento que o PythonC e o Jython é 0,8 vezes mais lento que o Java. Meus testes confirmam os dados do Jython mas neles o Java foi cerca de 2,5 vezes mais lento.

* Embora a portabilidade do C/C++ passe por recompilção do código e o programador tenha que se importar em manter o código portável não existe plataforma que não possua um compilador C/C++ e na verdade mesmo as funções que dependem do sistema operacional são relativamente parecidas e podem ser portadas usando o famoso #ifdef QQOS. Já o Java hoje roda em um número menor de plataformas que o Python embora seu objetivo principal seja a portabilidade. Tem certeza disso? Seria melhor exibir os fatos aqui. -- OsvaldoSantanaNeto

** O Java tem, hoje, um número ligeiramente maior de bibliotecas que o Python mas como não se integra ao C/C++ (Embora exista o JNI para esta integração ele não tem sido estimulado ou sequer visto com bons olhos ,embora esta seja minha opinião eu só conheço dois trabalhos com ele o Tk para Jython e o sqlite para Java) limita. Já o Jython tem quase todas as extensões do Python mais as do Java ou seja só perde para a linguagem C.

Ok. Não ter seu uso estimulado é diferente de não ter. Você poderia dizer que integrar Java e C/C++ não é tão simples como integrar Python com C/C++. Além disso, SWT também usa Java e C/C++. -- OsvaldoSantanaNeto.

*** Como o Python se integra a bibliotecas feitas em C/C++ o seu limite se expansão é praticamente infinito o que o coloca em posição de vantagem.

Seria bom que você reavaliasse toda essa seção do seu texto. Me parece que existem incoerências nos resultados e, em se tratando de comparar linguagens, é bom tomar bastante cuidado. Caso contrário você pode levantar a ira dos Lemmings da Sun e nesta situação apenas dados reais e bem palpáveis, sem marketing é que poderá 'derrotá-los'. -- OsvaldoSantanaNeto

IntroducaoJythonRodrigo (editada pela última vez em 2008-09-26 14:06:56 por localhost)