- 1 декабря 1999 г.
JPython: Удачный союз Python и Java.<br>(Фрагмент Гл. 10 "Среды и приложения" книги "Изучение Python")
В этом номере мы продолжаем публикации на тему альтернативных реализаций
языка Python. В первой статье рубрики, представляющей собой фрагмент главы из
книги "Изучение Python", речь пойдет о реализации Python на Java-машине. Эта
реализация, как уже известно нашим читателям, получила название JPython, позже
преобразованное в Jython. В данном случае авторы рассматривают преимущества
языка на конкретном примере, приводят коды и иллюстрации результатов работы
программ на JPython.
JPython представляет собой последнюю вышедшую версию Python, написанную на Java Джимом Хьюдженином. Сообщества Python и Java очень заинтересованы разработкой JPython. Пользователи Python рады возможности перенести свои сегодняшние знания по языку Python в основанные на Java среды разработки, а программисты Java - что могут использовать скриптовый язык Python в своих Java-системах для тестирования библиотек и изучения Java-библиотек из интерпретируемой среды.
JPython можно получить на http://www.python.org/jpython с лицензией и условиями распространения, аналогичными имеющимся для CPython (который часто сравнивают с JPython).
JPyhon содержит несколько элементов:
jpython
, являющийся эквивалентом программы на Python, использующийся на протяжении всей книги.jpythonc
, перекомпилирующий программу на JPython в файлы классов Java. Полученные в результате файлы классов Java могут использоваться как любые другие файлы классов Java, например, как апплеты, сервлеты или beans.- Набор модулей, обеспечивающий пользователей JPython подавляющим большинством модулей из стандартной библиотеки Python.
- Несколько программ, демонстрирующих различные аспекты программирования на JPython.
Использование JPython во многом сходно с использованием Python:
~/book> jpython
JPython 1.0.3 on java1.2beta4
Copyright 1997-1998 Corporation for National Research Initiatives
>>> 2 + 3
5
- JPython пока еще медленнее, чем CPython. То, насколько он медленнее, во многом зависит от использованного тестового кода и от Виртуальной Машины Java, которую использует JPython. С другой стороны, автор JPython сделал ряд многообещающих оптимизаций, которые могут сделать будущие версии JPython такими же или даже более быстрыми, чем CPython.
- Некоторые из встроенных или библиотечных модулей недоступны для JPython.
Например, до сих пор не реализован вызов
os.system()
, поскольку сделать это достаточно трудно, учитывая взаимодействие Java с лежащей в ее основе операционной системой. Кроме того, некоторые из наиболее крупных модулей расширения (таких, как среда Tkinter GUI framework) отсутствуют, так как в Java не существует средств, лежащих в их основе (набора инструментов Tk/Tcl toolkit в случае Tkinter).
JPython предоставляет программистам Python доступ к библиотекам Java
Самое важное различие между JPython и CPython, тем не менее, состоит в том, что JPython предлагает программисту Python непосредственный доступ к библиотекам Java. Рассмотрим программу jpythondemo.py, результат работы которой представлен на Рис. 10-5.
![](/images/Pic_10-5_st_Python_XML_2001_2_1.gif)
Рис. 10-5. Результат работы jpythondemo.py
from pawt import swing
import java
def exit(e): java.lang.System.exit(0)
frame = swing.JFrame('Swing Example', visible=1)
button = swing.JButton(This is a Swinging button!', actionPerformed=exit)
frame.contentPane.add(button)
frame.pack()
Эта простая программа демонстрирует, насколько просто создавать программы на
Python, использующие среду
Swing Java GUI
framework. Первая строка импортирует Java-пакет swing
(модуль
pawt
вычисляет точное местонахождение Swing, который может
находиться в java.awt.swing
, в com.sun.java.swing
или, возможно, в javax.swing
). Вторая строка импортирует пакет
java
, необходимый нам для вызова
java.lang.System.exit()
. Четвертая строка создает рамку
JFrame
, присваивая ее свойству visible
значение
"истина". Пятая строка создает кнопку JButton
с меткой и
определяет, какая функция должна вызываться при нажатии этой кнопки. И наконец,
последние две строки помещают кнопку JButton
в рамку
JFrame
и делают это все видимым.
Опытные программисты Java, возможно, будут несколько удивлены некоторыми
фрагментами кода в jpythondemo.py, поскольку они имеют ряд отличий от
эквивалентной им Java-программы. Для того чтобы упростить использование
библиотек Java пользователями Python, JPython выполняет большой объем работы
незаметно для них. Например, когда JPython импортирует пакет Java, он активно
исследует соответствующий пакет, и затем, используя Java Reflection API,
находит содержимое пакетов и подписи классов и методов. Кроме того, JPython "на
лету" конвертирует между собой типы Python и Java. В jpythondemo.py,
например, текст кнопки ('This is a Swinging example!'
)
представляет собой строку Python. Перед вызовом конструктора для
JButton
, JPython находит наиболее подходящий конструктор
(например, отклоняя версию, принимающую Icon в качестве первого аргумента), и
автоматически конвертирует строковый объект Python в строковый объект Java.
Более сложные механизмы позволяют конструктору JButton
принимать
ключевой параметр actionPerformed=exit
. Такой стиль невозможен в
Java, поскольку Java не имеет возможности манипулировать функциями (или
методами) как объектами первого класса. JPython исключает необходимость
создания класса ActionListener
с единственным методом
actionPerformed
, хотя при желании вы можете использовать более
многословную форму.
JPython как скриптовый язык Java
Растущая популярность JPython объясняется тем, что он позволяет программистам исследовать бесчисленное множество библиотек Java, которые становятся доступны в интерактивной среде. Кроме того, доказано, что очень удобно, когда Python встроен в качестве скриптового языка в среды Java, для настройки, тестирования и других программистских задач, выполняемых пользователями (в противоположность системным разработчикам). Чтобы увидеть пример интерпретатора Python, встроенного в Java-программу, см. программу в директории demo/embed дистрибутива JPython.
Реальное приложение JPython/Swing: grapher.py
Программа grapher.py (результат работы которой показан на Рис. 10-6)
предоставляет пользователям возможность графически исследовать поведение
математических функций. Она также создана на основе набора инструментов Swing
GUI toolkit. В ней есть два окошка для ввода текста, в который должен вводиться
код на языке Python. Первый текст представляет собой произвольную программу на
языке Python, вызваемую перед тем, как отображать функцию; он импортирует
необходимые модули и определяет любые методы, которые могут пригодиться при
вычислении значений функции. Вторая текстовая область (помеченная как
Expression:
) должна содержать выражение (но не утверждение) на
языке Python (как в sin(x)
). Оно вызывается для каждой точки
данных, с параметром, равным координате по оси x
.
Рис. 10-6. Результат работы grapher.py
Пользователь может контролировать стиль графика (линией или с заполнением),
дискретность и цвет. И, наконец, пользователь может сохранять конфигурацию на
диск и снова загружать ее (с помощью модуля pickle
). Вот код
программы grapher.py:
from pawt import swing, awt, colors, GridBag
RIGHT = swing.JLabel.RIGHT
APPROVE_OPTION = swing.JFileChooser.APPROVE_OPTION
import java.io
import pickle, os
default_setup = """from math import *
def squarewave(x,order):
total = 0.0
for i in range(1, order*2+1, 2):
total = total + sin(x*i/10.0)/(float(i))
return total
"""
default_expression = "squarewave(x, order=3)"
class Chart(awt.Canvas):
color = colors.darkturquoise
style = 'Filled'
def getPreferredSize(self):
return awt.Dimension(600,300)
def paint(self, graphics):
clip = self.bounds
graphics.color = colors.white
graphics.fillRect(0, 0, clip.width, clip.height)
width = int(clip.width * .8)
height = int(clip.height * .8)
x_offset = int(clip.width * .1)
y_offset = clip.height - int(clip.height * .1)
N = len(self.data); xs = [0]*N; ys = [0]*N
xmin, xmax = 0, N-1
ymax = max(self.data)
ymin = min(self.data)
zero_y = y_offset - int(-ymin/(ymax-ymin)*height)
zero_x = x_offset + int(-xmin/(xmax-xmin)*width)
for i in range(N):
xs[i] = int(float(i)*width/N) + x_offset
ys[i] = y_offset - int((self.data[i]-ymin)/(ymax-ymin)*height)
graphics.color = self.color
if self.style == "Line":
graphics.drawPolyline(xs, ys, len(xs))
else:
xs.insert(0, xs[0]); ys.insert(0, zero_y)
xs.append(xs[-1]); ys.append(zero_y)
graphics.fillPolygon(xs, ys, len(xs))
# draw axes
graphics.color = colors.black
graphics.drawLine(x_offset,zero_y, x_offset+width, zero_y)
graphics.drawLine(zero_x, y_offset, zero_x, y_offset-height)
# draw labels
leading = graphics.font.size
graphics.drawString("%.3f" % xmin, x_offset, zero_y+leading)
graphics.drawString("%.3f" % xmax, x_offset+width, zero_y+leading)
graphics.drawString("%.3f" % ymin, zero_x-50, y_offset)
graphics.drawString("%.3f" % ymax, zero_x-50, y_offset-height+leading)
class GUI:
def __init__(self):
self.numelements = 100
self.frame = swing.JFrame(windowClosing=self.do_quit)
# build menu bar
menubar = swing.JMenuBar()
file = swing.JMenu("File")
file.add(swing.JMenuItem("Load", actionPerformed = self.do_load))
file.add(swing.JMenuItem("Save", actionPerformed = self.do_save))
file.add(swing.JMenuItem("Quit", actionPerformed = self.do_quit))
menubar.add(file)
self.frame.JMenuBar = menubar
# create widgets
self.chart = Chart(visible=1)
self.execentry = swing.JTextArea(default_setup, 8, 60)
self.evalentry = swing.JTextField(default_expression,
actionPerformed = self.update)
# create options panel
optionsPanel = swing.JPanel(awt.FlowLayout(
alignment=awt.FlowLayout.LEFT))
# whether the plot is a line graph or a filled graph
self.filled = swing.JRadioButton("Filled",
actionPerformed=self.set_filled)
optionsPanel.add(self.filled)
self.line = swing.JRadioButton("Line",
actionPerformed=self.set_line)
optionsPanel.add(self.line)
styleGroup = swing.ButtonGroup()
styleGroup.add(self.filled)
styleGroup.add(self.line)
# color selection
optionsPanel.add(swing.JLabel("Color:", RIGHT))
colorlist = filter(lambda x: x[0] != '_', dir(colors))
self.colorname = swing.JComboBox(colorlist)
self.colorname.itemStateChanged = self.set_color
optionsPanel.add(self.colorname)
# number of points
optionsPanel.add(swing.JLabel("Number of Points:", RIGHT))
self.sizes = [50, 100, 200, 500]
self.numpoints = swing.JComboBox(self.sizes)
self.numpoints.selectedIndex = self.sizes.index(self.numelements)
self.numpoints.itemStateChanged = self.set_numpoints
optionsPanel.add(self.numpoints)
# do the rest of the layout in a GridBag
self.do_layout(optionsPanel)
def do_layout(self, optionsPanel):
bag = GridBag(self.frame.contentPane, fill='BOTH',
weightx=1.0, weighty=1.0)
bag.add(swing.JLabel("Setup Code: ", RIGHT))
bag.addRow(swing.JScrollPane(self.execentry), weighty=10.0)
bag.add(swing.JLabel("Expression: ", RIGHT))
bag.addRow(self.evalentry, weighty=2.0)
bag.add(swing.JLabel("Output: ", RIGHT))
bag.addRow(self.chart, weighty=20.0)
bag.add(swing.JLabel("Options: ", RIGHT))
bag.addRow(optionsPanel, weighty=2.0)
self.update(None)
self.frame.visible = 1
self.frame.size = self.frame.getPreferredSize()
self.chooser = swing.JFileChooser()
self.chooser.currentDirectory = java.io.File(os.getcwd())
def do_save(self, event=None):
self.chooser.rescanCurrentDirectory()
returnVal = self.chooser.showSaveDialog(self.frame)
if returnVal == APPROVE_OPTION:
object = (self.execentry.text, self.evalentry.text,
self.chart.style,
self.chart.color.RGB,
self.colorname.selectedIndex,
self.numelements)
file = open(os.path.join(self.chooser.currentDirectory.path,
self.chooser.selectedFile.name), 'w')
pickle.dump(object, file)
file.close()
def do_load(self, event=None):
self.chooser.rescanCurrentDirectory()
returnVal = self.chooser.showOpenDialog(self.frame)
if returnVal == APPROVE_OPTION:
file = open(os.path.join(self.chooser.currentDirectory.path,
self.chooser.selectedFile.name))
(setup, each, style, color,
colorname, self.numelements) = pickle.load(file)
file.close()
self.chart.color = java.awt.Color(color)
self.colorname.selectedIndex = colorname
self.chart.style = style
self.execentry.text = setup
self.numpoints.selectedIndex = self.sizes.index(self.numelements)
self.evalentry.text = each
self.update(None)
def do_quit(self, event=None):
import sys
sys.exit(0)
def set_color(self, event):
self.chart.color = getattr(colors, event.item)
self.chart.repaint()
def set_numpoints(self, event):
self.numelements = event.item
self.update(None)
def set_filled(self, event):
self.chart.style = 'Filled'
self.chart.repaint()
def set_line(self, event):
self.chart.style = 'Line'
self.chart.repaint()
def update(self, event):
context = {}
exec self.execentry.text in context
each = compile(self.evalentry.text, '<input>', 'eval')
numbers = [0]*self.numelements
for x in xrange(self.numelements):
context['x'] = float(x)
numbers[x] = eval(each, context)
self.chart.data = numbers
if self.chart.style == 'Line':
self.line.setSelected(1)
else:
self.filled.setSelected(1)
self.chart.repaint()
GUI()
Логика этой программы очень проста, и имена классов и методов облегчают ее
понимание. Большая часть могла бы быть написана на практически аналогичном
(только чуть более длинном) коде Java. Кстати, здесь виден выигрыш от
использования языка Python: вверху модуля определены значения по умолчанию для
элементов управления Setup
и Expression
. Первый из
них импортирует функции из модуля math и определяет функцию с именем
squarewave
. Второй описывает вызов этой функции со специфическим
параметром order
(с ростом этого параметра получающийся в
результате график все больше и больше напоминает меандр, отсюда и имя функции).
Если у вас установлены Java, Swing и JPython, вы можете экспериментировать и с
другими возможностями Setup
и Expression
.
Важнейшее преимущество использования JPython вместо Java в данном примере
состоит в методе update
: он просто вызывает утверждение Python
exec
с кодом Setup
в качестве аргумента, и затем
вызывает eval
с уже откомпилированным кодом
Expression
для каждой координаты. Пользователи могут свободно
вставлять любую часть кода Python в эти текстовые окна!
JPython все еще в значительной степени находится в процессе разработки; Джим Хьюдженин постоянно совершенствует и оптимизирует интерфейс между Python и Java. Кроме того, JPython, будучи второй реализацией Python, стимулировал Гвидо ван Россума отделить ключевые для языка аспекты Python от тех, что представляют собой просто особенности его реализации. К счастью, Джим и Гвидо, похоже, работают сообща и достигают согласия по большей части вопросов.
От авторов перевода.
Данная статья была написана в апреле прошлого года. С тех пор JPython сделал несколько больших шагов вперед. Проект сейчас носит название Jython и 31 декабря вышел релиз 2.0, который синхронизирован с версией CPython 2.0. В него добавлены списочные встраивания, расширенные операторы вызова, печати и присваивания. Он также включает модули регулярных выражений Apache Jakarta ORO regular expression modules. Если Вы работаете с Java или планируете в будущем -
Автор: Марк Латц, Дэйвид Эшер