This book assumes its audience has previous Java knowledge, or that the reader is acquiring Java knowledge from additional sources. What this means is that object-oriented programming (OOP) requires no introduction. If you know Java, you know objects. The terms class, instance, encapsulation, polymorphism, and inheritance all permeate Java discussions so there is only need for brief mention here. This chapter begins with an overview of encapsulation, abstraction, and information hiding followed by the definition of Jython classes. This quick start due to previous OOP knowledge does not mean that Jython classes and instances are the same as Java’s. Differences do exist, and using both Jython and Java classes within Jython creates a duality that you should keep in mind while reading on. This duality means references to a class’s properties may be unique to Java or Jython so always consider the context in which the class occurs. Fortunately Jython does not alter the nature or behavior of Java classes, so confusion concerning context is unlikely.
After describing how to define Jython classes and discussing Jython class attributes, the details concerning Jython’s inheritance, method overloading, constructors, and destructors appear. To complete the introduction to Jython classes, this chapter concludes with a plethora of example Jython classes.
This section is really about name mangling and electric fences, but let’s start with a review of more traditional object terms. Encapsulation is the combining, or aggregation, of multiple entities to produce a new, often higher-order entity such as a class. A class encapsulates data and related functionality into a single object. A class also provides abstraction by organizing functionality in a way that simplifies the interface and hides the details of the implementation. The battle against complexity continues with information hiding—showing users only what they need to see in the name of simplicity. Java, among other languages, takes an additional step to enforce abstraction with attribute permissions modifiers like private and protected—the electric fence approach. Despite the immediate appeal electric fences have when facing software complexity, others contend that such strictness is unnecessary, that the electric fence should merely be warning signs that the adventurous continue past at their own risk. This analogy serves to contrast Java’s strict approach with Jython’s privacy mechanisms, which are quite open and contrary to those in Java.
Programmers can see and change most everything in Jython classes. A Jython convention for method and variable names implies privacy, though. An attribute that begins with one underscore is internal to the class. This is only by convention, not de jure. You can call attributes beginning with an underscore with impunity, but convention dictates you do so at your own risk. Jython variables that begin with two underscores are one step closer to enforced privacy, but it is only privacy through obscurity. The obscurity referred to is name mangling. Jython mangles the names of Jython class and instance attributes that begin with two underscores to encourage programmers to respect their privacy. The actual mangling is the addition of an underscore and the class name to the beginning of the attribute’s name. The variable __var
within the class A
becomes _A__var
. Mangling is the end of the protection— there is no further enforcement. Attributes are accessible using this mangled name, as can be seen in this example:
>>> class A: ... __classVar = "A class variable designated as private" ... def __init__(self): ... self.__instVar = "An instance variable designated as private" ... def __privateMethod(self): ... print "This method is designated as private." ... >>> inst = A()
>>> inst._A__classVar 'A class variable designated as private' >>> inst._A__instVar 'An instance variable designated as private' >>> inst._A__privateMethod() This method is designated as private.
Jython, in other words, doesn’t work so hard to protect programmers from themselves. The complete digest of Jython’s privacy mechanisms amounts to one underscore for “don’t use unless you’re sure of what your are doing” and two underscores for “don’t use unless you’re really sure of what you are doing.”
Another naming convention is two preceding and trailing underscores. This convention designates special or magic attributes that have special meaning in Jython, such as the __doc__
attribute. Therefore, the use of variables with two leading and trailing underscores is unwise unless implementing predefined special attributes. More details of Jython’s special class attributes appear in the following chapter.
Jython class definitions occur within modules, in the interactive interpreter, or in dynamically generated code.The syntax of a class definition follows:
"class" class_name[(bases)]: code block
The name class is the statement that begins the definition. The class name is any legal Jython identifier. Following the class name is an optional list of base classes, within parenthesis. A colon designates the end of the class statement and the beginning of the associated code block. A near-empty Jython class looks like this:
>>> class emptyClass: ... """This is a near-empty Jython class""" ...
Dissecting this empty class reinforces the essential parts of a class definition. The word class starts the class definition. The identifier for the class is emptyClass
. This class is not a subclass of any other classes because there is no parenthesis and list after its identifier. As is always the rule in Jython, the code block obeys indention delimiting, beginning with the code block being one indention level in from the class statement. We also see that the class is not quite empty because it has a documentation string. Just like functions and modules, a literal string at the beginning of a class code block becomes that class’s __doc__
attribute, or docstring
in Python lingo.
A class is a callable object, and calling a class returns an instance object. To call our example class, emptyClass
, add empty parenthesis after its name; emptyClass
does not have a constructor that requires a certain number of arguments, so an empty argument list suffices. To confirm the distinction between the class emptyClass
and the instance object it returns, use the built-in function type()
as is done here:
>>> c = emptyClass() >>> type(emptyClass) <jclass org.python.core.PyClass at 3838683> >>> type(c) <jclass org.python.core.PyInstance at 7833967>
What good is an empty class? Not much in Java, but Jython allows arbitrary changes to a Jython class. The emptyClass
example lacks attributes except for __doc__
. However, dynamically assigning attributes to emptyClass
or an instance of emptyClass
is possible because of Jython’s dynamic nature. This example demonstrates:
>>> emptyClass.z = 30 >>> c = emptyClass() >>> c.x = 10 >>> c.y = -20 >>> c.__doc__ = "This was a near-empty Jython class" >>> c.x 10 >>> c.y -20 >>> c.z 30 >>> c.__doc__ 'This was a near-empty Jython class'
Such arbitrary additions to Java classes do not work:
>>> from java.applet import Applet >>> a = Applet() >>> dir(a) [] >>> a.a=10 Traceback (innermost last): File "<console>", line 1, in ? TypeError: can't set arbitrary attribute in java instance: a
Although it is clear that calling a class returns an instance of that class, the clarity of Jython’s class-instance relationship from then on is wanting. Both the class and the instance have attributes unique from the other (Python literature uses attributes like Java literature uses members.) An instance attribute is unique and separate from a class attribute of the same name. Java designates class members with the use of the static
keyword, but Jython has no such keyword. Because Jython lacks explicit modifiers that designate whether an attribute is class specific or unique to an instance, the guidelines that determine this require delineation. The following lists pertain only to Jython classes. Using Java classes and instances within Jython does not change the behavior of Java’s class and instance members. The guidelines to creating Jython class and instance attributes are as follows:
All methods are instance methods.
Defining a data objects within the class, but not within a method, makes it a class attribute.
class A: x = "This is a class attribute"
Assigning to a data object qualified, or prefixed, with the class name makes it a class attribute.
>>> class A: ... def aMethod(self): ... A.x = "This is a class attribute"
Assigning to an identifier qualified, or prefixed, with the instance reference, or name, makes it an instance attribute.
>>>class A: ... def aMethod(self): ... "Hint: 'self' is the instance ref-- more on this later." ... self.x = "This is an instance attribute"
The guidelines to accessing class and instance attributes are as follows:
An identifier qualified with a class’s name references a class attribute.
>>> class A: ... x = "Class attribute" ... def aMethod(self): ... self.x = "Instance attribute" ... print A.x ... >>> test = A() >>> test.aMethod() Class attribute
An identifier qualified with an instance’s name may reference an instance attribute or a class attribute. If the instance attribute exists, it is used. An instance attribute shadows, or hides from accessibility, any class attribute of the same name. If no instance attribute of that name exists, the identifier references a class attribute. This requires two examples to clarify: the first prints the class attribute, but the second example adds an instance attribute of the same name to show how it precedes the class attribute in the lookup order.
>>> # Example 1- Only a class attribute "x" exists >>> class A: ... x = "Class attribute" ... def aMethod(self): ... print self.x ... >>> test = A() >>> test.aMethod() Class attribute >>> >>> # Example 2- Both a class and instance attribute "x" exist >>> class A: ... x = "Class attribute" ... def aMethod(self): ... self.x = "Instance attribute" ... print self.x ... >>> test = A() >>> test.aMethod() Instance attribute
The only tricky portion to remember is that self.x
references a class attribute, but assigning to self.x
creates a new instance attribute of that name that shadows the class attribute. One advantage of this is that immutable class attributes can serve as initial default values for instance attributes of the same name.
After reviewing the list, what seems to be missing? A Python programmer is tempted to answer “Nothing.” A Java programmer, however, will inevitably answer “Class static methods.” While numerous mechanisms to add this functionality have floated around, there is no standard, built-in support for class static methods in Jython classes. Despite recurrence of the idea, there is also no clamoring to add such support because module-level functions suffice.
All instances of a class share class attributes. On the other hand, instance attributes are unique to each instance of a class. Consider the class myFiles
, which contains a class attribute definition like this:
>>> class myFiles: ... path = "/home/rbill"
Because the path
is a class attribute, all instances of the class myFiles
share this same path
object. However, if an instance creates an instance attribute named path
, it is not only unique from the class attribute of the same name, but is also unique from all other instances of the myFiles
class. To confirm path
is class static requires creating two instances, changing the path, and then checking the value of path
in each instance:
>>> c1 = myFiles() >>> c2 = myFiles() >>> myFiles.path = "c:\windows\desktop" >>> c1.path "c:\windows\desktop" >>> c2.path "c:\windows\desktop"
Confirming the uniqueness of instance variables requires three instances. Remember that assignment to an identifier that is qualified, or prefixed, with the instance name, creates an instance attribute. This is equally true outside of the class definition and is used here to example instance variables:
>>> class myFiles: ... path = "/home/rbill" ... >>> c1 = myFiles() >>> c2 = myFiles() >>> c3 = myFiles() >>> c2.path = "c:\windows\desktop" # creates a new instance attrib >>> c3.path = "/export/home/solaris" # creates a new instance attrib >>> >>> c1.path # class attrib '/home/rbill' >>> c2.path # instance attrib unique to c2 'c:\windows\desktop' >>> c3.path # instance attrib unique to c3 '/export/home/solaris'
Defining instance methods in a Jython class is similar to writing a Jython function. Jython methods use the very flexible parameter scheme already described for Jython functions. Jython methods can use ordered arguments and keyword arguments, parameters can have default values, and you can allow for unknown numbers of parameters with the *
and **
designations in the method signature.The difference centers on the instance reference. In Java, the variable this
references the specific instance in which it is contained. this
is assumed, or implicit in Java, but Jython does no such implicit name binding for the instance object. Instead, every method in a Jython class must specify an identifier for the instance object in the first slot of the method’s parameter list. While this first slot is an arbitrary identifier, the word self
has become standard practice. An instance method that creates and prints an instance variable looks like the following:
>>> class test: ... def imethod(self): ... self.message = "This is an instance variable" ... print self.message ... >>> t1 = test() # create the instance >>> t2 = test() # create a second instance for comparison >>> t1.imethod() This is an instance variable >>> >>> # To prove the "message" data is unique to only instance "t1"... >>> t1.message 'This is an instance variable' >>> t2.message # imethod not called- so t2.message is not defined Traceback (innermost last): File "<console>", line 1, in ? AttributeError: instance of 'test' has no attribute 'message'
You’ll notice that the method definition of imethod
is the same as a function definition except that the added self
parameter (or other suitable identifier) is required to meet the instance obligation. Within the class’s code block, references to instance attributes must be qualified with self
, there is no implied understanding of other attributes contained within the object as is the case in Java. Consider a class with two methods called methodA
and methodB
. Because self
must be explicit in attribute lookups, any reference to methodA
from within methodB
must use self.methodA
. The Calendar
class below serves to demonstrate the self
prefix in instance references:
>>> class Calendar: ... def setDay(self, day): ... self.day = day ... def setMonth(self, month): ... self.month = month ... def getDay(self): ... return self.day ... def getMonth(self): ... return self.month ... def getDayandMonth(self): ... return self.getDay() + " " + self.getMonth() ...
Setting and retrieving the instance variables day
and month
both use the self
prefix in the Calendar
class. Note that the use of other instance methods, as is done in getDayandMonth
, also requires the self
prefix to qualify other instance methods.
Methods have their own local namespace, just like functions, so only variables within the instance and outside the method’s local namespace require the self
prefix. An example follows:
>>> class test: ... def methA(self): ... data = 10 # This is local to methA ... self.data = 20 # this is a separate instance variable
Class and instance attributes are stored in the object’s dictionary, or __dict__
variable. This means that attribute B
in class A
is available through A.__dict__[B]
. The built-in function vars()
is designed to return the __dict__
attribute of objects and is useful in exploring objects.
A constructor is a method that initializes an instance of a class. A constructor in Jython is a method named __init__
defined within a Jython class. This __init__
method’s parameters look similar to any other method’s parameters in that the first slot belongs to the instance object usually called self
. A constructor may not return a value other than None
. The return statement is allowed in a constructor and is useful in cases where a certain condition should return control before completing the entire __init__
block. If return
appears, it must be simply return
or returnNone.
A Jython class with a constructor that creates some instance variables looks like this:
>>> class contact: ... def __init__(self): ... self.name = "" ... self.phone = "" ...
The number of parameters defined in a constructor determines how many arguments appear when calling a class. If a constructor requires two parameters plus the self
slot, two arguments must appear when calling that class. If a contact
class required a name and phone number as constructor parameters, it would look like this:
>>> class contact: ... def __init__(self, name, phone): ... self.name = name ... self.phone = phone ...
This contact
class uses the constructor to initialize the name and phone variables to the name and phone constructor parameters. Creating an instance of this new contact class now requires two parameters and looks like this:
>>> c = contact("Someone's name", "555-555-1234") >>> # Or with keyword arguments >>> c = contact(phone="555-555-1234", name="Someone's name")
Supplying default values makes constructor parameters optional. Java can have multiple constructors to implement differing parameters. Jython cannot, but it does allow for great flexibility with parameters, including default values in methods like the constructor. Optional constructor parameters look like this:
>>> class contact: ... def __init__(self, name="", phone=""): ... self.name = name ... self.phone = phone ...
Now calling the contact
class can take any one of the following forms:
>>> c = contact() >>>c = contact("Someone's name") >>>c = contact("Someone's name","555-555-1234") >>>c = contact(name ="Someone's name") >>>c = contact(phone ="555-555-1234") >>>c = contact(name="Someone's name", phone="555-555-1234")
A common use for a constructor is establishing a database connection that stays connected for the life of the object. The following example contains a constructor that demonstrates establishing a database connection at the object’s instantiation. Note that this example depends on the MySQL database and its associated Java driver, both of which are available from http://www.mysql.org/.
>>> import java >>> import org.gjt.mm.mysql.Driver >>> from java.sql import DriverManager >>> class DBAdapter: ... def __init__(self, dbname): ... conString = "jdbc:mysql://localhost/%s" %% dbname ... self.db = DriverManager.getConnection(conString, "", "") ... print "DB connection established"
After the DBAdapter
class is instantiated, any instance method can access the database connection through the instance variable self.db
. The time-consuming connection process is therefore only incurred once per instance.
Constructors often need to call superclass constructors. To do so, use the classname
, followed by the __init__
method, and no matter what the parameters are, make the first parameter self
. The syntax looks like this in Jython:
Superclass.__init__(self, otherArgs)
A more specific example is sub-classing the java.net.Socket
class. The Socket
class in Java has no such method as __init__
, but the same syntax still applies. Jython internals handle which constructor to call. The following example demonstrates sub-classing the Socket class. Note that this example assumes you have an active Internet connection.
>>> from java.net import Socket >>> class SocketWrapper(Socket): ... def __init__(self, host, port): ... Socket.__init__(self, host, port) ... >>> s = SocketWrapper("www.digisprings.com", 80)
The syntax used to initialize a superclass is not restricted to just Java superclasses. Initializing a Jython superclass works the same way, and even makes more sense considering its constructor really is __init__
.
A finalizer or destructor is a method that if defined, is called when the object is collected by garbage collection. Jython uses Java’s garbage collection (gc), so when a Jython object becomes unreachable, and memory needs trimming, the JVM gc thread may reclaim that object. There are two things to remember when using finalizers in Jython: First, they incur a performance penalty, and secondly, there is no way to tell when, or if, an object will be finalized.
A Jython finalizer is an instance method named __del__
. This method looks like this:
>>> class test: ... def __del__(self): ... pass # close something, or clean up something here ...
The “Constructors” section demonstrated the creation of a database connection in a constructor, but to truly make the class complete, that connection should be closed in a finalizer or in some other method that is explicitly called. The following example demonstrates closing a database connection in the object finalizer, but there are some caveats in doing this that are described following the example:
>>> import java >>> import org.gjt.mm.mysql.Driver >>> from java.sql import DriverManager
>>> class DBAdapter: ... def __init__(self, dbname): ... conString = "jdbc:mysql://192.168.1.77/%s" %% dbname ... self.db = DriverManager.getConnection(conString, "", "") ... print "DB connection established" ... def __del__(self): ... if not self.db.isClosed(): ... self.db.close() ...
Java’s garbage collection, the nature of database resources and database licenses often conflict with the above approach. Because the Java virtual machine, and thus Jython, provides no guarantees as to when, or even if, garbage collection collects an unused object, relying on finalizers is dangerous. If an application creates numerous objects with database connections that are closed in finalizers, any delays in collecting such objects waste limited database resources. This is most evident where database licenses restrict the number of connections, but it is always a concern. The preceding example clarifies finalizers, but is not a prudent example of managing database connections. More on databases, including more clever resource management appears in Chapter 12, “Server-Side Web Programming.”
We’ve seen that the ability to set an arbitrary attribute is one difference between Java and Jython classes. The explicit use of the self
variable is another difference between Jython and Java. Clues pointing to a third difference between Jython and Java appeared early in the section Defining Jython Classes. Note that the definition of a Jython class allows a list of base classes. The use of the word “list” should pique interest for those accustomed to Java’s enforcement of single-inheritance. Single-inheritance means that a Java class can only inherit from a single base class. A Java class can implement multiple interfaces, but can only subclass a single Java class. Jython, on the other hand, allows multiple-inheritance but still must comply with Java’s restrictions. This again introduces the Java-Python duality found in Jython.
Jython’s inheritance capacity depends on the type of superclass. Because Java enforces single inheritance, Jython may only inherit from a single Java class; however, because Python allows multiple inheritance, Jython may inherit from multiple Python (Jython) classes. This duality makes Jython unique. It is not fully like Python, C++, and other languages that allow multiple inheritance because the number of Java superclasses is restricted. Additionally, it is not fully like Java because it may inherit from multiple Jython classes. Despite being restricted to a single Java base class, a Jython class may multiply-inherit from a single Java class along with multiple Jython classes (and Java interfaces). What follows are details concerning inheriting from Jython classes, Java interfaces and Java classes.
A Jython class need not inherit from any but can inherit from one, two, or many Jython base classes. Defining base, or super, classes occurs in the parentheses following the class identifier in a class definition. If you wish to inherit from the Thread
class within the threading module and inherit from the scheduler
class within the sched
module, you can use:
>>> from sched import scheduler >>> from threading import Thread >>> class threadedScheduler(scheduler, Thread): ... pass ...
Inheriting from multiple classes allows you to use methods from either base class or override methods from either base class. There are two ways to use, or call, a method defined in a base class from within a descendant. One is using the base class name and method name in dotted notation with the self
instance object as the first argument.
>>> class parentClass: ... def parentMethod(self): ... print "Parent method called" ... >>> class child(parentClass): ... def callSuper(self): ... parentClass.parentMethod(self) ... >>> c = child() >>> c.callSuper() Parent method called
Explicitly calling a superclass with the current instance object as the first parameter as demonstrated just above has two purposes. First, this is how you initialize superclasses in Jython. Second, this allows you to call a superclass’s method even if it is overridden in the current class. The following example demonstrate the latter with a revised child class:
>>> class parentClass: ... def parentMethod(self): ... print "Parent method called" ... >>> class child(parentClass):
... def parentMethod(self): ... print "Overridden parent method" ... def callSuper(self): ... parentClass.parentMethod(self) ... >>> c = child() >>> c.callSuper() Parent method called
The other way to call a method in a superclass is using the self
prefix. To call method B
in the superclass, use self.B()
then allow the object attribute lookup mechanisms to locate the method. This assumes that method B
does not exist in the child instance:
>>> class Super: ... def B(self): ... print "B found in superclass" ... >>> class A(Super): ... def callSuper(self): ... self.B() ... >>> test = A() >>> test.callSuper() B found in superclass
Attribute lookup for those identifiers qualified with the self
prefix begins in the current instance, then proceeds through superclasses according to the order they are listed in the class definition. Therefore, changing the order of superclasses may change which attribute is found (assuming multiple superclasses define the same attribute). An example of this follows:
>>> class SuperA: ... def A(self): ... print "Test method from SuperA" ... >>> class SuperB: ... def A(self): ... print "Test method from SuperB" ... >>> class C(SuperA, SuperB): ... def callSuper(self): ... self.A() ... >>> test = C() >>> test.callSuper() Test method from SuperA >>> >>> class C(SuperB, SuperA): ... def callSuper(self): ... self.A() ... >>> test = C() >>> test.callSuper() Test method from SuperB
In Java, you implement an interface rather than subclass it. However, there currently is no explicit interface equivalent in Jython, or Python, versions 2.1 and earlier. Interfaces, or protocols, are currently only implied in Jython and Python. This means that to implement a Java interface in Jython, you use the same syntax as if the interface were a base class. Just as Java can implement multiple interfaces, you can inherit from multiple Java interfaces. The combination of multiple Jython classes and multiple Java interfaces is also acceptable:
>>> from java.text import CharacterIterator >>> from java.io import Serializable >>> from java.io import DataInput >>> from threading import Thread >>> class test(CharacterIterator, Serializable, DataInput, Thread): ... pass ... >>> t = test()
Because Java enforces single-inheritance, Jython classes are also restricted to at most one Java class as a base class. An attempt to inherit from more that one Java class raises an exception:
>>> from java.applet import Applet >>> from java.util import Vector >>> class a(Applet, Vector): ... pass ... Traceback (innermost last): File "<console>", line 1, in ? TypeError: no multiple inheritance for Java classes: java.util.Vector and java.applet.Applet
A Jython class that inherits from a Java class can also inherit from interfaces and Jython classes however. A sneaky few will try to fake multiple inheritance by using a Java class and another Java class wrapped in a Jython class, but this does not work:
>>> from java.awt import Checkbox >>> from java.awt import Label >>> class labelWrapper(Label): ... pass ... >>> class labelCheckbox(Checkbox, labelWrapper): ... pass ... Traceback (innermost last): File "<console>", line 1, in ? TypeError: no multiple inheritance for Java classes: org.python.proxies.__main__$labelWrapper$0 and java.awt.Checkbox
Multiple inheritance from a Java class and a Jython class works, but only applies if the Jython class does not inherit from a Java class as is the case with Jython’s Thread
class:
>>> from threading import Thread >>> from java.awt import Label >>> class threadedLabel(Label, Thread): ... pass ... >>> tl = threadedLabel()
Listing 6.1 is a module that contains an example of a class that inherits from a Java class. A full dissection of this follows starting with imports, then the class definition and the two instance methods within the FileFilter
class. This example is a Jython module that should be placed somewhere on the sys.path
so that the discussion can continue by importing the filefilter
module.
The filefilter
module imports the following items:
from java import io
—The Filter
class inherits from the Java class java.io.FilenameFilter
, and this inheritance requires that the base class first be imported.
from os import path
—This enables the use of the path.join
function. This function joins path elements, adding platform specific separators where appropriate. The Filter
class applies regular expressions to entire paths, so this necessitates joining the path and filename elements.
import re
—This imports Jython’s regular expression module. The FileFilter
class approves only those files that match a list of specified regular expressions. Remember that a regular expression is different that glob wildcards. The asterisk (*
) is a repeating operator in regular expressions, and the period (.
) matches any character.
The FileFilter
class in Listing 6.1 inherits from the Java class java.io.FilenameFilter
so that a java.io.File
class can use it to retrieve a filtered directory listing. The list is filtered according to which files return 1
when applied to the accept
instance method.
Of the two instance methods in Listing 6.1, addFilter
allows the loading of regular expressions into an instance variable, but accept
is the method required to satisfy the role as a FilenameFilter
. The instance variable self.ffilter
is a PyList
that holds the compiled regular expressions. This instance variable is created when the first filter is added, and that is the reason for the if 'ffil
ter' not in vars(self).keys()
condition. A similar condition appears in the accept
method as part of an assert
statement that ensures at least one regular expression is added before using the filter.
Example 6.1. A File-Filtering Class that Uses a Java Base Class
# file: filefilter.py from java import io from os import path import re class FileFilter(io.FilenameFilter): def addFilter(self, ffilter): if 'ffilter' not in vars(self).keys(): self.ffilter = [] self.ffilter.append(re.compile(ffilter)) def accept(self, dir, name): assert 'ffilter' in vars(self).keys(),'No filters added' for p in self.ffilter: if not p.search(path.join(str(dir), name)): return 0 return 1
To use this new Jython module and class in the interactive interpreter, ensure that it is in the sys.path
, and then type:
>>> import filefilter >>> from java import io >>> ff = filefilter.FileFilter() >>> ff.addFilter(".*.tar.gz") >>> ff.addFilter("eiei") >>> # Change the following line to use a path specific to your platform >>> L = io.File("c:\windows\desktop").list(ff) >>> >>> # print the list of files found >>> for item in L: ... print item ... eieio-0_16.tar.gz
Working with Java classes requires extra care. First, Java types require translation into Jython classes, and vice-versa, for method parameters and return values. Information about how Jython and Java types translate is available in Chapter 2, “Operators, Types, and Built-In Functions.” Calling members of a Java superclass requires careful explanation. There are two syntaxes for calling an attribute in a base class. The first is the class name plus method in dotted syntax with self
supplied as the first argument, as in the following:
>>> from java import util >>> class test(util.Vector): ... def addToSuper(self, objectToAdd): ... "Calling a java superclass looks like this:" ... util.Vector.addElement(self, objectToAdd) ... >>> t = test() >>> t.addToSuper("a string") >>> t.toString() '[a string]'
The problem with this first syntax is that the Java superclass does not see the call as originating from a subclass. This syntax does work for any public method, and it is required when explicitly initializing Java superclasses, but it does not work for protected instance methods—use the second syntax for that.
The second syntax is self.method
. This self
prefixed syntax ensures Java sees the call as originating in a subclass, which is required to access protected instance methods. Listing 6.2 demonstrates accessing members of a Java superclass using both syntaxes.
Example 6.2. Calling Methods in a Java Superclass
# file: javabase.py import sys from java import util class cal(util.GregorianCalendar): def __init__(self): # This uses the "classname.method(self)" syntax # to initialize the superclass util.GregorianCalendar.__init__(self) def max(self): # This uses the "classname.method(self)" syntax return util.GregorianCalendar.getActualMaximum(self, 1) def min(self): # Another example of "classname.method(self)" syntax return util.GregorianCalendar.getActualMinimum(self, 1) def compute1(self): # This tries the "classname.method(self)" syntax. # This will not work because the method called is protected. try: util.GregorianCalendar.computeTime(self) return "Success" except AttributeError, e: print "compute1 failed. ", e def compute2(self): # This uses the "self.method()" syntax so that the call appears # to originate from the base class. try: self.computeTime() return "success" except AttributeError, e: print "compute2 failed.", e Assuming that the above module is in the sys.path, you can test it with this: >>> import javabase >>> c = javabase.cal() >>> print "Trying max: ", c.max() Trying max: 292278994 >>> print "Trying min: ", c.min() Trying min: 1 >>> print "Trying compute1: ", c.compute1() Trying compute1: compute1 failed. class 'java.util.GregorianCalendar' has no attribute 'computeTime' None >>> print "Trying method compute2: ", c.compute2() Trying method compute2: success
Java’s protected
and static
modifiers are of special concern to Jython subclasses. Explaining this requires a quick peek under the hood of Jython. How does Jython access Java superclass class members? Some of the access is through a proxy object, which is the real subclass of the Java object, but some access is not through the proxy. Using self.add
in a subclass of java.util.Vector
(that does not override add
) actually asks the proxy object to call the superclass
method. The proxy, being the real
subclass, has access to protected instance methods. However, the current implementation does not create proxy entries for class fields, or static members. What does this mean? It means that when a Jython subclass tries to access static methods or class fields, it does not occur from a real Java subclass. This makes protected fields, and protected static members the equivalent of private as far as the Jython subclass is concerned.
Here’s a quick list of what a Jython subclass does have access to:
Public class (static) methods and fields
Public instance methods and fields
Protected instance methods (only with the self.method
notation.)
Here is a list of what a normal Java subclass has access to, but a Jython subclass does not:
Protected class (static) methods and fields
Protected instance fields
Package-protected members
Such limitations are normally handled one of two ways. First, you can write Java classes to mediate access to required protected fields and protected static methods that you need. A section on trees at the end of this chapter demonstrates a Java class written to mediate inaccessible members. Second, there is the Jython registry setting python.security.respectJavaAccessibility
. The Jython registry file is a file called registry
in Jython’s installation directory, or a file called .jython
in a user’s directory. The respectJavaAccessibility
setting within this file can be true or false. If false, Jython gains access to protected and private members of Java classes (not just superclasses). As you might expect, there are additional implications for security and stability when circumventing accessibility restrictions this way. Also, requiring subsequent users of your work to also set respectJavaAccessibility
to false might be troublesome. These issues require consideration when deciding to change the respectJavaAccessibility
property.
A final note on using Java superclasses involves superclass constructors. Explicitly calling a Java superclass constructor uses the classname.__init__(self)
syntax.Without an explicit call, an empty superclass constructor is called after completion of the subclass’s __init__
method.
Java uses method overloading extensively. Method overloading allows, or requires, multiple methods with the same name for differing parameter lists. The differences in the parameter lists are the number of parameters and the parameter types. For example, the java implementation of Jython’s xrange
function requires three methods to implement:
public static PyObject xrange(int n) { return xrange(0,n,1); } public static PyObject xrange(int start, int stop) { return xrange(start,stop,1); } public static PyObject xrange(int start, int stop, int step) { return new PyXRange(start, stop, step); }
While this is pervasive in Java, Jython does not provide for method overloading. The lack of method overloading certainly does not diminish Jython’s expressiveness, but it does become an issue when working with Java classes. What happens when Jython code calls an overloaded Java method? It generally works as expected. Despite Jython’s lack of method overloading, it does do a good job of using overloaded methods in Java classes. An example is this usage of the class java.lang.StringBuffer
:
>>> import java >>> sb = java.lang.StringBuffer() >>> sb = java.lang.StringBuffer(10) >>> sb = java.lang.StringBuffer("Jython ") >>> sb.append(2.1) Jython 2.1 >>> sb.append("b") Jython 2.1b >>> sb.append("1") Jython 2.1b1
The StringBuffer
class has three different constructors, and the append method is also overloaded, but Jython employs all methods without trouble. Internally Jython identifies which Java method to call first by the number of parameters, then by types. Implementing overloaded Java methods does not mean overloading is possible in Jython classes, however. Because users can invoke overloaded Java methods from Jython, they assume overriding only a certain Java method in a Jython subclass should work. This is not true. When a Jython subclass overrides an overloaded Java method, it must handle all overloaded cases of that method. Jython does not employ method overloading by type signatures at all in this case. Even if it appears that the number of parameters should explicitly select a particular method, Jython remains non-selective. It’s all or nothing when overriding an overloaded method.
Listing 6.3 examines subclassing the java.awt.Dimension
class. Listing 6.3 extends the Dimension class to make a similar class with boundaries. The interesting methods in Listing 6.3 are the constructor and setSize
. The constructor establishes maximum sizes with optional parameter values, or a default of 100
. The constructor also must initialize the superclass, which actually has three constructors. The subclass allows for each of the java.awt.Dimension
constructors depending on what arguments it receives. Additionally, the setSize
method of BoundaryBox
overrides the superclass’s methods of the same name. The superclass, Dimension
, overloads the setSize
method with these two type signatures:
void setSize(Dimension d) void setSize(double width, double height)
Remember that overriding an overloaded method shadows all superclass methods of the same name. While specific usage of a class may mean some of these methods are insignificant to the situation, it is often the case that a new method within the subclass must handle multiple, or all superclass method signatures of the same name. In this case, it should allow for width, height values, or a Dimension instance in the setSize
method. Listing 6.3 does allow for either of these arguments, but because of the limit testing, Listing 6.3 only need use one of the superclass’s setSize
methods.
Example 6.3. Overriding Overloaded Java Methods
# file: boundarybox.py from java import awt import types import warnings as wrn # to shorten some lines class BoundaryBox(awt.Dimension): """BoundaryBox is a java.awt.Dimension subclass with max limits. The constructor accepts optional width and height values or a java.awt.Dimension instance, which provide max values""" def __init__(self, *args): if len(args)==2: # assume this is width, height self.maxWidth = args[0] self.maxHeight = args[1] awt.Dimension.__init__(self, args[0], args[1]) elif len(args)==1: # assume this is a Dimension instance self.maxWidth = args[0].getWidth() self.maxHeight = args[0].getHeight() awt.Dimension.__init__(self, args[0]) elif len(args)==0: self.maxWidth = 100 self.maxHeight = 100 awt.Dimension.__init__(self) def setSize(self, *dim): if len(dim) == 2: # args must be width, height w = self._testWidth(dim[0]) h = self._testHeight(dim[1]) elif len(dim) == 1 and isinstance(dim, awt.Dimension): # in case arg is a Dimension instance w = self._testWidth(dim.getWidth()) h = self._testheight(dim.getHeight()) else: assert 0, "'setSize accepts w, h or a Dimenstion inst" awt.Dimension.setSize(self, w, h) def _testWidth(self, w): if w > self.maxWidth: msg = "Width, %s, exceeds bounds. Changed to %s" print msg % (w, self.maxWidth) return self.maxWidth return w # width within bounds def _testHeight(self, h): if h > self.maxHeight: msg = "Height, %s, exceeds bounds. Changed to %s" print msg % (h, self.maxHeight) return self.maxHeight return h # height within bounds
To test the BoundaryBox
class, place the boundarybox.py
file in the sys.path
, and use the following:
>>> import boundarybox >>> bb = boundarybox.BoundaryBox(100, 200) >>> bb.setSize(50, 201) Height, 201, exceeds bounds. Changed to 200 >>> bb.setSize(101, 100) Width, 101, exceeds bounds. Changed to 100 >>> bb.width 100 >>> bb.height 100
To better understand Jython classes, it is best to examine demonstrations. This section provides demonstrations of not only Jython classes, but how they relate to similar Java classes.
This example compares a Java implementation with similar functionality in Jython. The differences noted in the comparison helps clarify usage of Jython classes.
When an application requires a single point of interaction with some specific object, Java programmers often create a class that restricts creation of more than one instance of itself. While there are different implementations of this, the most frequent seems to be combining a class field and class method to return the instance. Listing 6.4 shows a Java class containing an instance of itself in a class field. This class also has a class method, getInstance
, which determines if an instance already exists before returning one. In this case, the class method returns a pre-existing instance if it already exists, but it is also common to raise an exception, or just return nothing.
Example 6.4. Java Singleton
# file: Singleton.java public class Singleton { private static Singleton single=null; private Singleton() {} public static Singleton getInstance() { if (single==null) single = new Singleton(); return single; } //The methods which required a single point of access //go here. }
This makes for an interesting comparison due to the lack of class methods and the inability to make a constructor private in Jython. How do you implement this in Jython? Using a non-constructor to return an instance is little value without a private modifier to restrict the actual constructor. A constructor cannot return a value, so it cannot choose a pre-existing instance to return. However, static class data attributes exist, and this can help test for an existing sequence. This only allows for a test, so this is most useful in an assertion or other expression that raises an exception when trying to create a second instance. This could look something like this:
# file: SimpleSingleton.py class Singleton: single = None def __init__(self): assert Singleton.single==None, "Only one instance allowed" Singleton.single = self def __del__(self): Singleton.single = None
The usage would raise an AssertionError
at the creation of a second instance:
>>> from SimpleSingleton import Singleton >>> s1 = Singleton() >>> s2 = Singleton() Traceback (innermost last): File "SimpleSingleton.py", line 11, in ? File "SimpleSingleton.py", line 4, in __init__ AssertionError: Only one instance allowed
While raising an exception is sufficient in many situations, you may want the pre-existing instance. To more closely mimic the Java class in Listing 6.4, you need a substitute for class methods. Java’s class-centric programming can dissuade some from module-level encapsulation and the use of functions, but you should be careful to not discount them. Combining a class and factory function within a module is often a solution, as it is here. Listing 6.5 shows this combination.
Example 6.5. Function + Class = Singleton
# file: singleton.py class _Singleton: single = None def __init__(self): pass # put required methods here def __del__(self): _Singleton.single = None def Singleton(): if _Singleton.single == None: _Singleton.single = _Singleton() return _Singleton.single #add testing code to the module if __name__=='__main__': s1 = Singleton() s2 = Singleton() # set an instance data attribute in s1 s1.data = 4 # s2 should point to the same data value if it is really # the same instance print s2.data
The result from running jython singleton.py
is merely the integer 4
, which confirms the same instance is shared by both the s1
and s2
identifiers in the preceding example.
Listing 6.6 is a simple class that searches lines or files for a specified pattern. The constructor only caches a list of files in the specified directory. The findFiles
method compares a pattern against this cached list (self.files
) and returns a filtered list. The findLines
reads each file line-by-line to return a list of lines containing the specified pattern. The two methods are most sensible used together by first limiting the file list with findFiles
, then searching those files with findLines
.
Example 6.6. Searching Files with a Jython Class
# file: grep.py import os from os.path import isfile class directoryGrep: def __init__(self, directory): self.files = filter(isfile, [os.path.join(directory, x) for x in os.listdir(directory)]) def findLines(self, pattern, filelist=None): """Accepts pattern, returns lines that contain pattern Optional second argument a filelist to search""" if not filelist: filelist = self.files results = [] for file in filelist: fo = open(file) results += [x for x in fo.readlines() if x.find(pattern) != -1] fo.close() # explicit close of file object return results def findFiles(self, pattern): "Accepts pattern, returns filenames that contain pattern" return [x for x in self.files if x.find(pattern) != -1] # test if __name__=='__main__': g = directoryGrep("c:\windows\desktop") files = g.findFiles(".py") print g.findLines("java", files)
The result from running jython
grep.py
on my machine is as follows:
['from java.lang import System ', 'from java.lang import Runtime ', 'from java.io import IOException ', 'import java ', 'from java.sql import DriverManager ']
When reading files, Listing 6.6 explicitly closes the file objects it opens. Python programmers very frequently use the following phrase:
open(file).readlines()
This creates and reads from an anonymous file object. This assumes that closing the file and releasing the resource occurs when garbage collection sees a need to collect it. Remember, Jython relies on Java garbage collection, so the actually closing of the file and releasing of the resource is delayed an unknown extent of time. The explicit closing of the object in Listing 6.6 avoids potential problems due to this latency.
HTTP headers consist of an actual request followed by a series of key-value pairs called headers and a blank line (
), which terminates the request header. Listing 6.7 eases the manipulation of request data by parsing strings into data fields, and back to a string.
Classes often help by mere containment and representation, meaning they group related data and can present that data in a useful way when needed. The containment in Listing 6.7 consists of two dictionaries, one containing request data, and the other containing headers. The interesting thing is that the dictionary containing the request data (method, URL, and HTTP version) is the instance dictionary, or self.__dict__
. Listing 6.7 sets entries in this instance mapping as if it were a normal dictionary with self.__dict__.setdefault
, and by using the instance attribute notation self.attribute = x syntax
. Both approaches update self.__dict__
.
Representation, normally the toString
method in a Java class, is the special method __repr__
in a Jython class. This method must return a string representation of an object if it exists, and when there is a need for a string representation of the object (like a print
statement), this is the method called to provide it. Constructors, finalizers, and this representation method are all the special class methods discussed so far, but numerous special methods for customizing an objects behavior exist. Consider __repr__
just foreshadowing of a much larger set of special methods discussed in Chapter 7, “Advanced Classes.”
Example 6.7. Parsing HTTP Request Headers
# file: request.py import string import re class Request: """Class for working with request strings: The constructor optionally takes an http request string. e.g., req = Request("GET http://localhost/index.html HTTP/1.0")""" def __init__(self, request=None): "Parses a raw request string, if supplied. self.headers = {} if not request: request = "GET None HTTP/1.0" self.method, self.url, self.version = request.split()) self.host = self.path = self.file = "" match = re.match("http://(S*?)(/.*)", self.url) if (match != None): self.host, self.path = match.groups() def setHeader(self, stringHdr): try: x,y = [x.strip() for x in stringHdr.split(":")] self.headers[x] = y except ValueError: raise SyntaxError("A header string must be " "in the format key : value") def __repr__(self): d = self.__dict__ # make a local copy for convenience request = " ".join([d['method'], d['url'], d['version']]) request += " " for key in self.headers.keys(): request += (key + ": " + self.headers[key] + " ") request += " " return request # module testing code if (__name__=='__main__'): # Creating the instance can be without arguments... a = Request() # or with a raw request string like... a = Request("GET HTTP://freshmeat.net/index.html HTTP/1.0") # Headers are added/altered with "setHeader". # setHeader works with a header string as an argument... a.setHeader("Accept: image/jpeg, image/pjpeg, image/png, */*") a.setHeader("Proxy-Connection: Keep-Alive") a.setHeader("Accept-Encoding : gzip") #put it all together print a
Output from running Jython request.py
is:
GET HTTP://freshmeat.net/index.html HTTP/1.0 Accept: image/jpeg, image/pjpeg, image/png, */* Accept-Encoding: gzip Proxy-Connection: Keep-Alive
Trees are common data structures that are excellent examples for demonstrating classes. Trees are collections of nodes and leaves, where a node contains other nodes and leaves, and a leaf terminates the branch (does not contain other nodes). The analogy more closely relates to family trees than the arborous alternative because of their top-down representation, and because the inter-relationship of nodes and leaves is often referred to as a parent-child relationship. Listing 6.8 defines two classes: leaf
and node
. The great thing about using trees is that nodes and leaves can have the same methods, so the interface for using a node
or a leaf
remains the same. This, combined with their inherent recursive structure makes for a clean, consistent, and widely applicable pattern. Hierarchical data such as taxonomies, company personnel, and filesystems are only a few examples of tree-like data. It’s possible to apply the generic leaf
and node
classes in Listing 6.8 to a great number of similar tree-like structures. You only need to know three things about trees to create appropriate classes.
A tree consists of nodes.
Some nodes can have children.
Leaves are nodes, but they are special because they cannot have children.
What does this mean for classes? It means there are specific expectations about what a class knows about itself and what functionality it provides. Questions a node object should be able to answer about itself are, Am I a leaf? Who’s my parent? and Who are my children? The behavior expected from a non-leaf node object is the ability to add and remove children. The leaf
class, despite not having children, also includes methods for adding and removing children, as well as answering,Who are my children? This ensures a consistent interface. These methods raise an exception, except in the case of the getChildren
method, which returns a tuple
so that it better matches node behavior. One additional requirement that is an implementation detail is that each object has a setParent
method, which is required when adding objects to a node.
Because a leaf node is just a node minus children, the leaf
class subclasses node
and overrides only those methods related to children.
Example 6.8. Making Trees with Jython Classes
# file: trees.py class node: def __init__(self, name, value=None): self._children = [] self.name = name self.value = value self._leaf = 0 # Means this node can have children # These methods answer question the object must know about itself. def isLeaf(self): """returns answer to, "Am I a leaf?" """ return self._leaf def getParent(self): """returns answer to, "Who is my parent?" """ return self._parent def getChildren(self): """returns answer to, "Who are my children?" """ return tuple(self._children) # These methods supply required functionality of the object def addChild(self, node): node.setParent(self) self._children.append(node) def removeChild(self, node): try: self._children.remove(node) return 1 except ValueError: # in case that child doesn't exist return 0 # implementation detail def setParent(self, parent): self._parent = parent # A quick way to view the tree def dump(self, level=0): print "%s%s %s" %% (" "*level, self.name, self.value or "") for child in self.getChildren(): child.dump(level+1) def __repr__(self): return self.name class leaf(node): def __init__(self, name, value=None): node.__init__(self, name, value) self._leaf = 1 def addChild(self, node): raise ValueError, "Cannot add children to a leafNode" def removeChild(self, node): raise ValueError, "A leaf node has no children to delete." def getChildren(self): return () # returns empty tuple # Code to test these two classes if __name__=='__main__': root = node("root", "trees") evergreens = node("evergreens") evergreens.addChild(node("Picea", "Spruce")) deciduous = node("deciduous") deciduous.addChild(node("Acer", "Maple")) quercus = node("Quercus", "Oak") quercus.addChild(leaf("Alba", "White Oak")) quercus.addChild(leaf("Palustris", "Pin Oak")) quercus.addChild(leaf("Rubra", "Red Oak")) cdeciduous.addChild(quercus) root.addChild(evergreens) root.addChild(deciduous) children = root.getChildren() print children print quercus.getParent() print # add blank line print "Here is a dump of the entire tree structure:" root.dump()
The results from running jython
trees.py
should look like:
(evergreens, deciduous) deciduous Here is a dump of the entire tree structure: root trees evergreens Picea Spruce deciduous Acer Maple Quercus Oak Alba White Oak Palustris Pin Oak Rubra Red Oak
The Java implementation of a similar tree would no doubt be different, but how it would be different is interesting to look at. A Java tree would likely have an abstract
class to ensure a consistent minimum for a legitimate node. Additionally, some instance variables would use the protected modifier. A potential abstract
class in Java would look something like Listing 6.9.
Example 6.9. An abstract Java Node Class
// file: Node.java import java.util.Vector; public abstract class Node { protected String name; protected Node parent = null; protected boolean leaf = false; public abstract Vector getChildren(); public abstract boolean addChild(Node n); public abstract boolean removeChild(Node n); public boolean isLeaf() { return leaf; } public String toString() { return name; } }
This introduces some problems for the Jython tree classes. Namely, the protected fields leaf
, parent
, and name
. A Jython subclass cannot access a protected Java field unless Jython’s respectJavaAccessibility
registry setting equals false. Let’s assume that sidestepping access modifiers with the registry setting is unacceptable for whatever reason. The alternative is to write a Java class to mediate Jython’s access to the abstract Node
class. Listing 6.10 shows just such a Java adapter
class.
Example 6.10. A Java-to-Jython adapter Class
// file: JyNodeAdapter.java import Node; import java.util.*; public abstract class JyNodeAdapter extends Node { protected void setLeaf(boolean leaf) { this.leaf = leaf; } protected void setName(String nodeName) { name = nodeName; } public String getName() { return name; } public Node getParent() { return parent; } public void setParent(Node p) { parent = p; } public void dump() { this.dump(0); } public void dump(int level) { for (int i = 0; i < level; i++) { System.out.print(" "); } System.out.println(name + " " + this.value); Vector v = getChildren(); for (Enumeration e = v.elements(); e.hasMoreElements();) { JyNodeAdapter node = (JyNodeAdapter)e.nextElement(); node.dump(level + 1); } } }
The only revisions required to make the Jython tree classes is to add JyNodeAdapter
as a superclass and alter attribute fetching and setting to use the appropriate set
and get
methods as designated in the Java superclasses. One additional change is altering the return value of getChildren
to always be a Vector (empty Vector for the leaf) to comply with the protocol specified in the abstract Node
class. Listing 6.11 shows the modified version of the Jython tree implementation with the appropriate modifications to use the Node
and JyNodeAdapter
base classes.
Example 6.11. Jython Subclass of the JyNodeAdapter
# file: SubclassedTree.py import JyNodeAdapter from java.util import Vector class JyNode(JyNodeAdapter): def __init__(self, name, value=None): self.setName(name) self.value = value self._children = Vector() def getChildren(self): return self._children def addChild(self, c): c.setParent(self) self._children.add(c) return 1 def removeChild(self, c): self._children.removeElement(c) return 1 def __repr__(self): return self.toString() class leaf(JyNodeAdapter): def __init__(self, name, value=None): self.setName(name) self.value = value self.setLeaf(1) def addChild(self, node): raise ValueError, "Cannot add children to a leafNode" def removeChild(self, node): raise ValueError, "A leaf node has no children to delete." return Vector() # returns empty Vector def __repr__(self): return self.toString() if __name__=='__main__': root = JyNode("root", "trees") evergreens = JyNode("evergreens") evergreens.addChild(JyNode("Picea", "Spruce")) deciduous = JyNode("deciduous") deciduous.addChild(JyNode("Acer", "Maple")) quercus = JyNode("Quercus", "Oak") quercus.addChild(leaf("Alba", "White Oak")) quercus.addChild(leaf("Palustris", "Pin Oak")) quercus.addChild(leaf("Rubra", "Red Oak")) deciduous.addChild(quercus) root.addChild(evergreens) root.addChild(deciduous) children = root.getChildren() print children print quercus.getParent() print # add blank line print "Here is a dump of the entire tree structure:" root.dump()
The results from running jython
SubclassedTree.py
should look like:
(evergreens, deciduous) deciduous Here is a dump of the entire tree structure: root trees evergreens Picea Spruce deciduous Acer Maple Quercus Oak Alba White Oak Palustris Pin Oak Rubra Red Oak