Appendix D. Java and Python: A Comparison

CONTENTS
  •  Python 101
  •  A GUI Application
  •  A Statistics Application
  •  A String Parsing Example
  •  Embedding Jython in Java

This appendix looks at the relative merits of Java and Python using the following tests as the basis for comparison: (1) a GUI application, (2) a statistics application, (3) a simple example of string parsing, and (4) an application with an embedded script.

Python 101

Here's a Python class followed by its Java equivalent.

Python:

class Employee:   def __init__(self, fname="John", lname="Doe", id=1, manager=None, dept=1):         self.__firstName     =     fname         self.__lastName      =     lname         self.__id            =     id         self.__manager       =     manager         self.__dept          =     dept   def getManager(self):         return self.__manager   def __str__(self):         values = self.__lastName, self.__firstName, self.__id         return join(values,',')

Java:

public class Employee{    private String firstName, lastName;    private int id, dept;    private Employee manager;    public Employee(){           firstName = "John";           lastName = "Doe";           id = 1;           vmanager=null;           dept=1;    }    public Employee(String fname, String lname, int id, Employee manager, int dept){           firstName     =     fname;           lastName      =     lname;           this.id       =     id;           this.manager  =     manager;           this.dept     =     dept;      }    public Employee getManager(){           return manager;      }    public String toString(){           StringBuffer buf = new StringBuffer();           buf.append(lastName+',');           buf.append(firstName+',');           buf.append(""+id);           return buf.toString();    }    ...    ... }

Similar to Java's this, Python's self is a reference to the class instance referred to by the first argument in each method. There's no separate declaration for member variables; they're declared when assigned a value. (You can declare class as well as instance variables.) Python's __str__() method is equivalent to Java's toString().

In Python, to create an instance of Employee and print it to the screen you enter the following:

print Employee()

The equivalent in Java is

System.out.println(new Employee());

Here's how to create two instances of Employee, joe and ron, and print them to the console. Joe is Ron's manager. We get him by invoking the ron instance's getManager() method.

Python:

joe = Employee("Joe", "Battista", 100) ron = Employee(manager=joe, id=101, lname="Furgeson", fname="Ron") print ron print ron.getManager()

Java:

Employee joe=new Employee("Joe","Batista",100,null,1); Employee ron=new Employee("Ron","Furgeson",101,joe,1); System.out.println(ron); System.out.println(ron.getManager());

As you can see, the syntax is similar.

Jython uses named arguments and default values. This means that when the ron instance is created the arguments are called out of order, which should be familiar to those of you who've used Visual Basic or VBScript. For those of you unfamiliar with this idea, think of it this way: You can call methods as you normally do in Java, or you can do it the Python way, passing the method name/value pairs and saving yourself some coding, not to mention headaches. Have you ever been stuck with several versions of the same method when all you wanted was different default values? Every default value is another overloaded method, which can get messy.

A good example of the use of named arguments is Python's GridBag utility class, which manages the infamous GridBagLayout manager. I've created something similar in Java that uses overloaded methods to create GridBag constraints.

A Simple GUI Prototype

We have our simple class. Now we'll create a simple GUI prototype. (Like the class, it's nonsensical since its only purpose is for illustration.)

Fire up the Python interactive interpreter, typing in Jython at the system prompt. Then import JFrame from javax.swing.

>>> from javax.swing import JFrame

Create an instance of the frame, set its size to 200,200, and make it visible.

>>> frame=JFrame("My Prototype",visible=1,size=(200,200))

This took only one line of code because in Jython any bean property of a class (by which I mean a property defined by getter and setter methods that is, the Java Bean design pattern for properties) can be set during the constructor call using named arguments.

Now let's add some components: labels, text fields, and a button. Import the necessary classes from javax.swing.

>>> from javax.swing import JButton,JTextField,JLabel,JPanel

We could have entered from javax.swing import *, as we do in Java, but that would have imported every class into our namespace, which Python considers bad style. Python's way is to let us view and manipulate a namespace.

>>> dir() ['JButton', 'JFrame', 'JLabel', 'JTextField', '__name__', 'frame']

Create a pane, using JFrame's contentPane property.

>>> pane = JPanel() >>> frame.contentPane.add(pane) javax.swing.JPanel[,0,0,0x0,invalid,layout=java.awt.FlowLayout,alignmentX= null,alignmentY=null,border=,flags=34,maximumSize=,minimumSize=,preferredSize= ,default Layout=java.awt.FlowLayout[hgap=5,vgap=5,align=center]]

If this were Java, we'd have to call frame.getContentPane() to create a pane because Java does not treat bean properties like instance variables.

To lay out the pane we're going to use the infamous GridBagLayout, which is the most complex of Jython's layout managers. To tame it we'll use the GridBag utility class. Notice how few lines of code it takes up.

Import the GridBag helper class. Then create an instance of it and associate it with the pane.

>>> from pawt import GridBag >>> bag = GridBag(pane)

Add the first component, a label, to GridBag, which will use all of GridBagConstraints's default values.

>>> bag.add(JLabel("Name")) >>> frame.validate()

Add another label on the second row of the grid.

>>> bag.add(JLabel("ID"), gridy=1) >>> frame.validate()

Add a text field on the first row in the second column, and pack the frame.

>>> name = JTextField(25) >>> bag.add(name, gridx=1, weightx=80.0) >>> frame.pack()

Add another text field for the employee ID to the right on the second row, and pack the frame.

>>> id = JTextField(10) >>> bag.add(id, gridx=1, gridy=1, weightx=80.0) >>> frame.pack()

Not what we want, is it? The text field components look silly because I accidentally (on purpose) aligned them centered, not at the left, in their cells. Let's fix this.

Remove the ID and name.

>>> pane.remove(id) >>> pane.remove(name)

Put them back with the correct alignment.

>>> bag.add(name, gridx=1, weightx=80.00, anchor='WEST') >>> bag.add(id, gridx=1, gridy=1, weightx=80.0, anchor='WEST') >>> frame.pack()

Jython handles bean events easily because it uses introspection and reflection to create event properties. With the JavaBeans Event design pattern, event properties equate to the method name in a method's event listener interface.

To demonstrate assigning a function or method to an event property, we'll set up an Okay button that prints an employee's name and ID when clicked.

Create and add a button to the GUI.

>>> okay = JButton("Okay") >>> bag.add(okay, gridx=1, gridy=2, anchor='CENTER') >>> frame.pack()

Create a function that prints out the value of the name and ID text.

>>> def handleOkay(event): ...     print "Name " + name.text ...     print "ID " + id.text ... >>> okay.actionPerformed=handleOkay

Enter some text in the name and ID fields, and hit Okay.

A GUI Application

Now let's create a GUI based on our prototype in both Jython and Java. The following example shows our Python employee form; after that is its Java equivalent.

Python:

from javax.swing import JFrame, JButton, JTextField, JLabel, JPanel from string import split from pawt import GridBag from Employee import Employee class EmployeeForm(JFrame):   def __init__(self):         JFrame.__init__(self, "Employee Form")         pane = JPanel()         self.contentPane.add(pane)         bag = GridBag(pane)               #Create a name and id text field.         self.__name = JTextField(25)         self.__id = JTextField(10)               #Create and add a "Name" and "ID" label.         name = JLabel("Name", labelFor=self.__name, displayedMnemonic=ord('N'))         bag.add(name)         id = JLabel("ID", labelFor=self.__id, displayedMnemonic=ord('I'))         bag.add(id, gridy=1)               # Add the name and ID text field to the form.         bag.add(self.__name, gridx=1, weightx=80.00, anchor='WEST')         bag.add(self.__id, gridx=1, gridy=1, anchor='WEST')               #Create an okay button, add it, and set up its event handler.         okay = JButton("Okay", mnemonic=ord('O'))         bag.add(okay, gridx=1, gridy=2, anchor='EAST')         okay.actionPerformed=self.handleOkay         self.visible=1         self.pack() def handleOkay(self, event):         fname, lname = split(self.__name.text, " ")         id = int(self.__id.text)         employee = Employee(fname, lname, id)         print employee if __name__=="__main__":EmployeeForm()

Java:

import javax.swing.*; import java.awt.GridBagLayout; import java.awt.GridBagConstraints; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import employee.Employee; public class EmployeeForm extends JFrame{    private JTextField name;    private JTextField id; public EmployeeForm(){    super("Employee Form");    JPanel pane = new JPanel();    getContentPane().add(pane);    pane.setLayout(new GridBagLayout());          // Create a name and id text field.    name = new JTextField(25);    id = new JTextField(10);          // Create and add a "Name" and "ID" label.    JLabel nameLabel = new JLabel("Name");    nameLabel.setLabelFor(name);    nameLabel.setDisplayedMnemonic('N');    GridBagConstraints constraint = new GridBagConstraints();    pane.add(nameLabel, constraint);    JLabel idLabel = new JLabel("ID");    idLabel.setLabelFor(id);    idLabel.setDisplayedMnemonic('I');    constraint.gridy=1;    pane.add(idLabel, constraint);          // Add the name and ID text field to the form.    constraint.gridy=0; constraint.gridx=1;    constraint.weightx=80.00;    constraint.anchor=GridBagConstraints.WEST;    pane.add(name, constraint);    constraint.gridy=1;    pane.add(id, constraint);          // Create an okay button, add it, and set up its event handler.    JButton okay = new JButton("Okay");    okay.setMnemonic('O');    constraint.gridx=1; constraint.gridy=2;    constraint.anchor=GridBagConstraints.EAST;    pane.add(okay, constraint);    okay.addActionListener(new ActionListener(){          public void actionPerformed(ActionEvent event){                handleOkay();          }    } );    this.setVisible(true);    this.pack(); } public void handleOkay(){    String name, fname, lname;    int index=0;    int id =0;    name = this.name.getText();    index = name.indexOf(" ");    fname = name.substring(0, index);    lname = name.substring(index+1, name.length());    id = Integer.parseInt(this.id.getText());    Employee employee = new Employee(fname, lname, id, null, 100);    System.out.println(""+employee); }    public static void main(String [] args){          new EmployeeForm();      } }

The Jython version is 1,290 characters; the Java version is 2,139 characters.

A Statistics Application

Remember the house price sample application we created in Chapter 4? You might want to go back and refresh your memory because we're going to be using it to continue our Python/Java comparison. I'm not going to repeat the information you'll find there, particularly the breakdown and explanation of the code, but I will highlight the important functions, as I did in Chapter 4, and show you the full code examples. A suggestion: Place a bookmark at the section on the house price example in that chapter for easy referral.

getRange()

The getRange() function iterates through a list of numbers passed to it to calculate the minimum and maximum values in a list. It returns these values, with their range, in a tuple. Here's one way to implement it:

def getRange (nums):    min = 300000000    max = -300000000    for item in nums:           if (item > max): max = item           if (item < min): min = item    return (min, max, max-min)

Here's another, better, way:

def getRange (nums):    return (min(nums), max(nums), max(nums)-min(nums))

With the first implementation, you have to be sure that the values passed to getRange() are within the minimum-maximum range. With the second implementation, you don't have to do that, thanks to the built-in min() and max() functions, which work with all numeric types.

getMean()

The getMean() function calculates the mean of a sequence of numbers by iterating through the sequence, summing the values, and then dividing the sum by the sequence length. It also determines if the result is a sample mean or a population mean.

def getMean (nums, sample):    sum = 0.0          # holds the value of sum           # iterate through the sequence # of numbers and sum them    for x in nums:           sum = sum + x           # Check to see if this is a sample mean    if(sample):           average = sum / (len(nums)-1)           # Else it is a population mean    else:           average = sum / len(nums)    return average

As with getRange(), Python has a better way to find the mean. It may surprise you.

def getMean (nums, sample):    sum = reduce(lambda a, b: a+b, nums)    if sample: average = sum / (len(nums)-1)    else: average = sum / len(nums)    return average

What's new here is the reduce() built-in function, which takes two arguments, function and sequence. function is specified with the lambda keyword, which makes it anonymous. reduce() applies function to two items in the sequence, cumulatively from left to right, reducing it to a single value, in our case sum.

The result is the same as with our first implementation of getMean(), but it's a lot shorter.

getMode()

The getMode() function finds the value that repeats most often in a sequence. First it duplicates the sequence (before modifying it); then it iterates through it, counting the occurrences of current items via the built-in count() method. Once an item is counted, it's removed from the duplicated sequence.

def getMode (nums):           #           # make a duplicate copy of the nums argument    duplicate = nums[:]    highest_count = -100    mode = None           #           # calculate the highest_count and the mode    for item in nums:           count = duplicate.count(item)           if (count == 0): continue           if (count > highest_count):                 highest_count = count                 mode = item           while(duplicate.count(item) > 0):                 duplicate.remove(item)    return mode

getMedian()

The getMedian() function finds the middlemost value once the sequence is sorted.

def getMedian (nums):    "Find the Median number"          # Create a duplicate since we are # going to modify it.    seq = nums[:]          # Sort the list of numbers.    seq.sort()    median = None # to hold the median value    length = len(seq) # to hold the length of the seq          # Check to see if the length is # an even number.    if ( ( length % 2) == 0):                  # Since it is an even number,                  # add the two middle number together.          index = length / 2          median = (seq[index-1] + seq[index]) /2.0    else:                  # Since it is an odd number,                  # just grab the middle number.          index = (length / 2)          median = seq[index]    return median

reportStatistics()

The reportStatistics() function calls all of the functions just described and stores their return values in two dictionaries, averages and ranges. It then places these dictionaries in another dictionary called report, which it returns.

def reportStatistics (nums):          # get central tendencies    averages = {          "mean":getMean(nums,0),          "median":getMedian(nums),          "mode":getMode(nums)          }          # get range    range = getRange(nums)          # put ranges in a dictionary    ranges = {          "min":range[0],          "max":range[1],          "range":range[2]          }    report = {          "averages": averages,          "ranges": ranges          }    return report

Notice that Python, unlike Java, has a built-in syntax that allows you to specify a dictionary (which is similar to a Java hashtable) with a literal.

runReport()

The runReport() function uses reportStatistics() to get and print the report dictionary.

from stat import reportStatistics house_in_awahtukee = [100000, 120000, 150000, 200000, 65000, 100000] report = reportStatistics(house_in_awahtukee) range_format = """ Range: The least expensive house is %(min)20.2f The most expensive house is %(max)20.2f The range of house price is %(range)20.2f """ average_format = """ Averages: The mean house price is %(mean)20.2f The mode for house price is %(mode)20.2f The median house price is %(median)20.2f """ print range_format % report["ranges"] print average_format % report["averages"]

The thing to notice here is the string format operator (%), which is like C's printf() function except that it can work with dictionaries. It's one of my favorite Python features because it makes text reporting so easy. I've even used it to generate Java code.

Here's Python's runReport() output:

Range: The least expensive house is          65000.00 The most expensive house is          200000.00 The range of house price is          135000.00 Averages: The mean house price is              122500.00 The mode for house price is          100000.00 The median house price is            110000.00

Here's the Java version.

package stat; import java.util.ArrayList; import java.util.Iterator; import java.util.Collections; import java.util.HashMap; public class Stats {   public static double getMean (ArrayList nums,                                           boolean sample){          // Define mean that finds two types of mean,          // namely:          // population mean and sample mean          double sum=0.0;          double average=0.0;          Iterator iterator = nums.iterator();          while(iterator.hasNext())                 sum = sum +                 ((Double)iterator.next()).doubleValue();                 // Check to see if this is a sample mean.          if(sample)                 average = sum / nums.size()-1;          else                 average = sum / nums.size();          return average;   }   public static ArrayList getRange (ArrayList nums){                 // Find the range. Returns a tuple with the                 // minimum, maximum, and range value          double min, max;          ArrayList ranges;          min =          ((Double)Collections.min(nums)).doubleValue();          max =          ((Double)Collections.max(nums)).doubleValue();          ranges = new ArrayList();          ranges.add(new Double (min));          ranges.add(new Double (max));          ranges.add(new Double (max-min));          return ranges;   }   public static double getMedian (ArrayList nums){                 // Find the Median number.                 // Create a duplicate since we are going to                 // modify the sequence.          ArrayList seq = new ArrayList(nums);                 // Sort the list of numbers.          Collections.sort(seq);          double median = 0.0; // to hold the median value          int length = seq.size(); // to hold the length of                                           // the sequence          int index=0;                 // Check to see if the length                 // is an even number.          if ( ( length % 2) == 0){                      // Since it is an even number,                      // add the two middle numbers together.                 index = length / 2;                 double m1 =                 ((Double)seq.get(index-1)).doubleValue();                 double m2 =                 ((Double)seq.get(index)).doubleValue();                 median = (m1 + m2) /2.0;          }          else{                      // Since it is an odd number,                      // just grab the middle number.                 index = (length / 2);                 median =                 ((Double)seq.get(index)).doubleValue();          }          return median;   }   private static int countMode(Object object,                                                 ArrayList list){          int index = 0;          int count = 0;          do {                 index=Collections.binarySearch(list,object);                 if(index >=0)list.remove(index);                 count++;          }          while (index >=0);          return count;   } public static double getMode (ArrayList nums){          // Find the number that repeats the most.                 // Make a duplicate copy of the                 // nums argument.          ArrayList duplicate = new ArrayList(nums);          Collections.sort(duplicate);          double highest_count = -100;          double mode = -100;          Iterator iterator = nums.iterator();                 // Iterate through nums removing                 // each item out of the duplicate.                 // Calculate the highest_count and the mode.          while(iterator.hasNext()){                 double count = 0;                 Object item = iterator.next();                 // Count the number of times the item                 // occurs in the list.                       // If Count is 0, go to the next                       // iteration.                 count = countMode(item, duplicate);                 if (count == 0) continue;                       // Determine the highest count. The                       // highest counted item is the mode.                 if (count > highest_count){                       highest_count = count;                       mode = ((Double)item).doubleValue();                 }          }          return mode;   }   public static HashMap reportStatistics(                                          ArrayList nums){                 // Get central tendencies.          HashMap averages = new HashMap();          averages.put("mean",                 new Double(getMean(nums,false)));          averages.put("median",                 new Double(getMedian(nums)));          averages.put("mode", new Double(getMode(nums)));                 // Get range.          ArrayList range = getRange(nums);          HashMap ranges = new HashMap();                 // Put ranges in a dictionary.          ranges.put("min", range.get(0));          ranges.put("max", range.get(1));          ranges.put("range",range.get(2));          HashMap report = new HashMap();          report = new HashMap();          report.put("averages", averages);          report.put("ranges", ranges);          return report;   } } --- RunReport.java --- package stat; import java.util.ArrayList; import java.text.MessageFormat; import java.util.HashMap; public class RunReport{   public static String range_format = "" +   "Range: \n"+   "The least expensive house is {0,number,currency} \n"+   "The most expensive house is {1,number,currency}  \n"+   "The range of house price is {2,number,currency} \n";   public static String average_format = "" +   "Averages: \n" +   "The mean house price is     {0,number,currency} \n"+   "The mode for house price is {1,number,currency} \n"+   "The median house price is     {2,number,currency} \n"; public static void main(String [] args){        ArrayList houses_in_awahtukee = new ArrayList();        houses_in_awahtukee.add(new Double(110000));        houses_in_awahtukee.add(new Double(190000));        houses_in_awahtukee.add(new Double(140000));        houses_in_awahtukee.add(new Double(120000));        houses_in_awahtukee.add(new Double(190000));        houses_in_awahtukee.add(new Double(180000));        houses_in_awahtukee.add(new Double(170000));        houses_in_awahtukee.add(new Double(180000));        houses_in_awahtukee.add(new Double(180000));        houses_in_awahtukee.add(new Double(190000));        houses_in_awahtukee.add(new Double(190000));        houses_in_awahtukee.add(new Double(250000));        HashMap report =               Stats.reportStatistics(houses_in_awahtukee);        HashMap ranges = (HashMap)report.get("ranges");        HashMap averages =               (HashMap)report.get("averages");        Object [] m_args = new Object[]{               averages.get("mean"),               averages.get("mode"),               averages.get("median")} ;        Object [] r_args = new Object []{               ranges.get("min"),               ranges.get("max"),               ranges.get("range")} ;        System.out.println(        MessageFormat.format(range_format,r_args));        System.out.println(        MessageFormat.format(average_format,m_args));   } }

Which one is shorter?

A String Parsing Example

To compare Java and Python string parsing, we're going to create a readable file consisting of a comma-delimited list of house prices.

100000,100000,120000,150000,170000,170000,80000,50000

Here's the Python code broken down:

Open the file.

>>> file = open("data.txt")

Read in the file data.

>>> data = file.read()

Import the split() function to parse the data.

>>> from string import split >>> housePrices = split(data, ",")

For demonstration, show that split() has split the data string into a list of strings.

>>> housePrices ['100000', '100000', '120000', '150000', '170000', '170000', '80000', '50000']

Convert housePrices from strings to floating-point values.

>>> housePrices = map(float, housePrices)

Show that housePrices is now a list of floating-point values.

>>> housePrices [100000.0, 100000.0, 120000.0, 150000.0, 170000.0, 170000.0, 80000.0, 50000.0]

Here's the actual code (from runReport2.py).

from stat import reportStatistics from string import split file = open("data.txt") data = file.read() housePrices = split(data, ",") housePrices = map(float, housePrices) report = reportStatistics(housePrices) range_format = """ Range: The least expensive house is %(min)20.2f The most expensive house is  %(max)20.2f The range of house price is  %(range)20.2f """ average_format = """ Averages: The mean house price is          %(mean)20.2f The mode for house price is      %(mode)20.2f The median house price is        %(median)20.2f """print range_format     % report["ranges"] print average_format      % report["averages"]

Here's the Java version of the RunReport2 module. Notice the difference in length.

package stat; import java.util.ArrayList; import java.util.HashMap; import java.util.StringTokenizer; import java.text.MessageFormat; import java.io.FileReader; import java.io.BufferedReader; public class RunReport2{   public static String range_format = "" +   "Range: \n"+   "The least expensive house is {0,number,currency} \n" +   "The most expensive house is {1,number,currency}  \n"+   "The range of house price is {2,number,currency}  \n"; public static String average_format = "" +   "Averages: \n" +   "The mean house price is {0,number,currency} \n"+   "The mode for house price is {1,number,currency} \n"+   "The median house price is {2,number,currency} \n";   public static void main(String [] args){          ArrayList houses_in_awahtukee = new ArrayList();          try {                BufferedReader reader=new BufferedReader(                                    new FileReader("data.txt"));                String data = reader.readLine();                StringTokenizer tokens=                       new StringTokenizer(data, ",\t\n\r ");                while (tokens.hasMoreTokens()){                       houses_in_awahtukee.add(                       Double.valueOf(tokens.nextToken()));                }          }          catch(Exception e){                e.printStackTrace();          }          HashMap report =                Stats.reportStatistics(houses_in_awahtukee);          HashMap ranges = (HashMap)report.get("ranges");          HashMap averages =                (HashMap)report.get("averages");          Object [] m_args = new Object[]{                averages.get("mean"),                averages.get("mode"),                averages.get("median")} ;          Object [] r_args = new Object []{                ranges.get("min"),                ranges.get("max"),                ranges.get("range")} ;          System.out.println(          MessageFormat.format(range_format,r_args));          System.out.println(          MessageFormat.format(average_format,m_args));   } }

Embedding Jython in Java

What follows is an example of embedding Jython in the Java code that ships with Jython's standard distribution. I've added comments.

import org.python.util.PythonInterpreter; import org.python.core.*; public class SimpleEmbedded {     public static void main(String []args) throws PyException {          // Create a Python interpreter.          PythonInterpreter interp = new PythonInterpreter();          System.out.println("Hello, brave new world");          // Execute an import statement and a          // print statement.          interp.exec("import sys");          interp.exec("print sys");          // Create a variable by assigning          // it the value 42.          // PyInterger is a Python integer.          interp.set("a", new PyInteger(42));          // Execute the print statement to          // print out the value of a.          interp.exec("print a");          // Assign x to equal the expression 2 + 2.          interp.exec("x = 2+2");          // Get the value associated with x.          PyObject x = interp.get("x");          // Print the value of x.          System.out.println("x: "+x);     } }

The preceding example doesn't do justice to the true ease of Java-Jython integration.

CONTENTS


Python Programming with the JavaT Class Libraries. A Tutorial for Building Web and Enterprise Applications with Jython
Python Programming with the Javaв„ў Class Libraries: A Tutorial for Building Web and Enterprise Applications with Jython
ISBN: 0201616165
EAN: 2147483647
Year: 2001
Pages: 25

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net