|
YOUR FEEDBACK
Did you read today's front page stories & breaking news?
SYS-CON.TV |
TOP THREE LINKS YOU MUST CLICK ON General Java Python Programming in the JVM
Python Programming in the JVM
By: Rick Hightower
Mar. 1, 2000 12:00 AM
What This Series Is About
There are some great languages that I didn't mention last month, but as I stated, the list wasn't comprehensive - I named less than 10% of all the languages for the JVM. And received my fair share of "Why didn't you mention language X?' Most of the languages covered by this series are scripting languages that are dynamic, interpreted and easy to program. For this article and the ones that follow, Java will be presented as the system language and the higher-level language will be presented as the "glue' language. Thus you define frameworks, libraries and components in Java and glue them together to make applications. This was described in detail in JDJ , Vol. 5, issue 2. The series will focus on topics such as other languages for the JVM; integrating Java with mainstream scripting languages like Perl and Python; special-purpose languages (rules, etc.); creating JavaServer Pages (JSP) in JavaScript, Webl and Python; SWIG; open-source Java initiatives; COM/DCOM from pure Java; and CORBA to legacy integration. JPython is the 100% Pure Java version of Python and is freely available - source code and all. An extremely dynamic, object-oriented language, JPython is in its second major release - JPython 1.1. Since JPython is the same syntax and language as Python, I'll use the terms interchangeably for the rest of this article. You've heard Java called a dynamic, object-oriented language. Well, JPython is more dynamic and more object-oriented than Java. In Python, unlike Java, everything is an object - classes, methods, namespaces are objects. Also, Python doesn't have any primitive types, and it supports multiple inheritance. In many ways Python is closer to Smalltalk than to Java - syntactically, however, Python is closer to Java than to Smalltalk. This isn't a case of my-language-can-beat-up-your-language syndrome. JPython doesn't replace Java; it augments it. Java and JPython have complementary roles - sometimes they overlap. JPython facilitates the following:
Python's ease of use is on a par with Visual Basic. Some say Python even surpasses Visual Basic because the syntax is more consistent and designed. However, unlike Visual Basic, Python is truly object-oriented. Others say that Python closely resembles a nonverbose version of Pascal or C++. The ease-of-use syntax is good, and the dynamic capabilities are extremely powerful. Put a Python in your toolbox.
Rosetta Stone
A Simple Class
Notice that the use of self is similar to the use of this in a Java class - self is the reference to the instance. The first argument to each method is a reference to self. Also note that there's no separate declaration for member variables, that is, they're declared when they're assigned a value. (You can declare class variables as well as instance variables.) The _str_method is a special method that acts like the toString method in Java. Compare the Python Employee class to the roughly equivalent Java class in Listing 2. To create an instance of an Employee and print it to the screen you'd do the following: print Employee() The equivalent Java statement would be: System.out.println(new Employee()); Next we create two instances of Employee called joe and ron, and print those employees to the console. We print joe, who is ron's manager, by invoking the getManager method of ron - first in JPython, then in Java
Python
Java As I said, the syntax is similar. One feature that JPython has is named arguments and default values. Notice that when the ron instance is created, the arguments are called out of order. If you've programmed in Visual Basic or VBScript, this concept should be familiar. If not, think of it this way: you can call methods as you normally do with Java, or you can pass name, value pairs to the method as shown above. This feature can save some coding effort, not to mention some headaches. Have you ever had several versions of the same method? And you just wanted to have different default values? Every default value is another overloaded method. It can get messy. A good example of using named arguments is the GridBag utility class that the JPython distribution provides. This utility helps you manage the infamous GridBagLayout. I've created something similar in Java that used overloaded methods to create GridBag constraints. I was amazed how short the GridBag utility was in Python. (It's in the pawt package, if anyone wants to check it out.)
A Simple GUI
If you have JPython installed, let's pretend that we're prototyping this GUI. Fire up the interactive interpreter by typing jpython as the system prompt. (This assumes that you've downloaded, installed and put JPython home directory on your Path. Follow the install instruction at www.jpython.org.) Import the JFrame from the javax.swing package. >>> from javax.swing import JFrame Create an instance of the frame, set its size to 200 by 200, then make it visible. >>> frame = JFrame("My Prototype", visible=1, size=(200,200)) You probably weren't expecting this to be only one line of code. In JPython any bean property of a class can be set during the call to the constructor using named arguments. By bean property I mean a property as defined by a getter and a setter method, that is, the bean "design pattern" for properties. At this point our frame is pretty boring. A stupid-looking gray box. Let's add some components to our stupid-looking gray box. We need to add some labels, text fields and an okay button. As we develop this GUI application I'll point out some of the features of JPython. As it's a nonsensical demo, we aren't going to meet any GUI style guidelines. First we need to import a few classes from javax.swing. >>> from javax.swing import JButton, JTextField, JLabel, JPanel Notice that we didn't use the * syntax. For example, we could have said "from javax.swing import * as you'd do in Java. But that would have imported every class into our namespace - in Python that would be considered bad style. In Python you can view and manipulate the namespace. For example, to see all of the variables in the current namespace, you can do the following: >>> dir() ['JButton', 'JFrame', 'JLabel', 'JTextField', '__name__', 'frame'] Thus, if we imported *, we'd have a lot of classes in our namespace, and that would be less than ideal. First create a pane. (Notice how the frame's contentPane property is handled; in Java you'd have to call frame.getContentPane() to get the contentPane. Bean properties are treated like instance variables.)
>>> pane = JPanel() For this example we'll use the GridBag utility class that's provided with JPython. GridBag makes using GridBagLayout easy. The amazing thing about GridBag is how few lines of code it took to write it - again, check it out. First we import the GridBag helper class, then create an instance of GridBag and associate it with the Pane. Please follow along in the interactive interpreter.
>>> from pawt import GridBag Now add the first component to the grid bag - a JLabel. This will use all of the default values of the GridBagConstraints (see Figure 1).
>>> bag.add(JLabel("Name")) Now add another JLabel. This time we add the label on the second row of the grid.
>>> bag.add(JLabel("ID"), gridy=1) Now add a text field on the first row in the second column. Then pack the frame (see Figure 2).
>>> name = JTextField(25) Now add a second text field for the employee ID, this time to the right on the second row. Then pack the frame (see Figure 3).
>>> id = JTextField(10) As you can see, this isn't what we want. The text field components are centered and look quite silly. I forgot to align the text field to the left in their cells (not really - I forgot on purpose). Let's remove the components and add them again with the proper alignment. Hopefully, you'll see how useful it is to be able to experiment with the layout in the interactive interpreter (see Figure 4). Remove the ID and name.
>>> pane.remove(id) Now re-add the ID and name with the proper alignment.
>>> bag.add(name, gridx=1, weightx=80.00, anchor='WEST') The above demonstrates the interactive, experimental environment. You can explore cause and effect without the normal recompile, retest environment. Bean events are handled easily in JPython. As with bean properties, JPython adds features to make event handling easier. First, JPython uses introspection and reflection to make event properties. Event properties equate to the names of the methods in the event listener interface for a given method using the event "design pattern" for JavaBeans. You can assign an event property a function or method. To demonstrate this, let's set up an okay button. When the okay button gets clicked, this prototype application will print out the employee's name and ID. First create and add a button to our GUI (see Figure 5).
>>> okay = JButton("Okay") Next create a function. The function prints out the value of the name and ID text.
>>> def handleOkay(event): Enter some text in the Name and ID field and hit the okay button. This is a simple session, creating a simple GUI. For those of you who followed along with JPython, let me know what you think of JPython. I like it and use it often.
Rosetta Stone GUI
Listing 3 shows the employee form that we prototyped in the interactive interpreter. Listing 4 shows the employee form in Java. The Java version is 2,139 characters while the JPython version is 1,290 characters; thus the Java version is 66% larger.
Rosetta Stone Statistics
Implementing getRange
First Try Implementing getRange
Let's break getRange down bit by bit. First getRange declares two variables called min and max. The min is to hold the minimum value. The max is to hold the maximum value.
min = 300000000 The min variable refers to a very large number so that the first item extracted from the list will be less than the large number and get assigned to the min value. The max value contains a very negative number so that the first item that gets extracted will be more than the large negative value and get assigned to min variable.
Technical Note: A better way to implement this code would have been to use the following:
from java.lang import Double
or:
from java.lang import Integer
and then:
min = Double.MAX_VALUE
or:
min = Integer.MAX_VALUE This would make the function work well with IntTypes or Double- Types, but what about LongTypes? Well, there's a more Python way of doing things, which will be explained shortly. Next, to figure the minimum and maximum number, the getRange function iterates through the nums sequence.
for item in nums:
if (item > max): max = item Notice the JPython for loop iterates through a sequence. A sequence is like a cross between a Java Array and a Java Vector - well, not exactly. The expression item > max determines if the item's value is greater then the value of max. If it is, it's assigned to the value of the item. This, so far, is a lot like Java. When the loop stops iterating the values, the getRange function returns the min, max and range (max - min) as:
return (min, max, max-min) This may be odd. Essentially, it appears that we're returning three values. Actually, we're returning a tuple of values. A tuple is an immutable sequence. Well, that was easy enough. If you read the technical note, you know this approach has some flaws. We're getting a variable called nums. The nums variable is a sequence. In Python, sequences have intrinsic operations (built-in functions) for finding the min and max values in a list. Therefore, we should have used the built-in function.
The Python Way of getRange
There's no more for loop, no more figuring out what the minimum or maximum variable should be initialized to. The built-in intrinsic functions in Python are very useful. Now this will work with longs, integers and floats. (Read the technical note under the first implementation for a description of the getRange problem).
Implementing getMean
This example shows example use of:
First create a variable called sum that holds the sum.
sum = 0.0 Then iterate through the nums sequence accumulating the value of the item x.
for x in nums: Next we check to see if this is a sample mean. If it is, we figure the average by dividing the sum by the number of items in nums less one, else we divide the sum by the number of items in the nums sequence.
if(sample):
# Else it is a population mean Last, we return the average. return average The foregoing is not much different from what you'd do in Java. You could say that it's very similar.
The Python Way of getMean
Examine the following code.
def getMean (nums, sample):
if sample: average = sum / (len(nums)-1)
return average
This approach uses a built-in function called reduce, which takes two arguments - a function object and a sequence object. The former is specified with the lambda keyword, which defines an anonymous function, that is, a function without a name. Thus: lambda a, b: a+b is equivalent to
def add(a,b): The reduce function applies the function argument to two items in the sequence argument cumulatively from left to right, which reduces the sequence to a single value that in our case is a sum. Thus the foregoing method is equivalent to the former getMean method, but a lot shorter. There are other built-in functions like reduce that provide functional programming features to Python.
Implementing getMode
This example shows example use of:
Implementing getMedian
This example shows example use of:
seq.sort() Next we get the length of the sequence and check to see if it's an even number with the expression length % 2. (Remember that the modulus operator - % - returns the remainder, so if length is even, the expression length % 2 will return a 0.) If the length is even, we calculate the median by adding the two most central numbers and figuring their average.
length = len(seq)
if ( ( length % 2) == 0): If the length is odd, we grab the middle value:
else:
index = (length / 2) Last, we return the median. return median
Implementing reportStatistics
This example shows example use of:
Let's cover the reportStatistics function step by step.
averages = { Get the range parameters - namely, the min, max and range - from the getRange function. Use the range[0], range[1] and range[2] items in the sequence returned from the function getRange. Note that "min":range[0] defines a key value pair in the ranges dictionary. Unlike Java, Python has built-in support for collections. Thus you can specify a dictionary, which is like a java.util.Hashtable, with a literal.
# get range
# put ranges in a dictionary Now define a dictionary called report that contains the averages and ranges dictionary:
report = { Last, let's return the report dictionary return report
Using reportStatistics
This example shows example use of:
As you can see, there are a lot of built-in functions and built-in collection types that make easy tasks easier and hard tasks possible. Now compare this to the Java version of this application in Listing 13. Notice how much shorter the Python version is.
Rosetta Stone String Parsing
For example, the file will contain: 100000,100000,120000,150000,170000,170000,80000,50000
The Python code to read this file in would be as follows: >>> file = open("data.txt") Read in the file data. >>> data = file.read() Import the split function to parse the data.
>>> from string import split For demonstration purposes show that the split function split the data string into a list of strings.
>>> housePrices Convert the housePrices list from a list of strings to a list of floating point values. >>> housePrices = map(float, housePrices) Show that the list is now a list of floating point values.
>>> housePrices Listing 14 is the listing for the foregoing prototype. Compare the JPython listing for runReport2 (Listing 14) and the Java listing (Listing 15) for runReport2. As before, the JPython version is much shorter.
Rosetta Stone Embedding JPython in Java
As you can see, it's relatively easy to embed JPython into a Java program. The example doesn't do the subject justice. In my book, Programming the Java APIs with JPython, there's an example that dynamically loads JPython servlets from a Java servlet using the embedding technique shown.
Vote for Your Favorite
Scorecard
Let's drill down a bit on the criteria I rated.
Parting Shots
JPython has a lot of momentum, and its syntax is mean and lean. It's to JavaBeans what Visual Basic is to ActiveX, and interest in it is growing. A recent poll at the NetBean's Web site showed JPython as the leading Java scripting language. In our poll at JDJ, JPython is neck and neck with NetRexx. Components have a symbiotic relationship with high-level languages. For example, Visual Basic did well because of ActiveX components. And ActiveX did well because of tools like Visual Basic. On the Java platform we have the component models; we need the glue, that is, tools for the high-level languages - debuggers, IDEs, and the like. JPython makes good glue, and it transcends the role of a glue language. For more information on JPython see Guido Van Rossum's article at www.developer.com/journal/techfocus/081798_jpython.html. See also www.jpython.org/ and www.python.org/. Many of the examples in this article were based on examples in my book, which is scheduled for publication in April from Addison Wesley Longman. YOUR FEEDBACK
LATEST JAVA STORIES & POSTS
SUBSCRIBE TO THE WORLD'S MOST POWERFUL NEWSLETTERS SUBSCRIBE TO OUR RSS FEEDS & GET YOUR SYS-CON NEWS LIVE!
|
SYS-CON FEATURED WHITEPAPERS MOST READ THIS WEEK SPONSORED BY INFRAGISTICS
BREAKING JAVA NEWS
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||