[Next] [Previous] [Top]

Deriving Built-In Classes in Python

1. Introduction

Over a year ago I started work on a set of Python bindings to Xt and Motif. The goal of the project was to make programming Xt applications in Python both easy and familiar for anyone who already had experience with Xt. The initial results were very promising but there were a few problems. In particular:

These problems could have been solved in a very straight forward way. All of the call data structures could have been bound simply as built-in types by using the struct member module. The symbolic constants could have been initialized by adding them to `.py' files created for that purpose. The widget class hierarchy could be represented by binding not just to the widgets, but also to the widget classes, with the widget class objects containing a pointer to the widget super class. Unfortunately, the first two of these straightforward solutions could not be considered adequate for a commercial application. The reasons for this are described below.

The struct member module, as useful as it is, has many limitations. The most obvious one is that it only supports a small number of data types and is not generally extensible. Since many Motif call data structures contain members that are structures, pointers, unions, enums, and even arrays, the straight forward approach would not be able to give adequate coverage and most call data structures would have had to be tediously bound to by hand.

Symbolic constants presented a different set of problems. Storing a set of symbolic constants as simple variables inside a module is subject to the user accidentally changing the value of what was intended to be a constant. It was also thought that users would expect a more symbolic treatment of symbolic constants from an interpreted environment. For example,

>>> print Xm.ALIGNMENT_CENTER
1
by itself is not all that bad, however it is much more likely that while developing or debugging a real application you would see something more like

>>> push_button.alignment = Xm.ALIGNMENT_CENTER
...
>>> print push_button.alignment
1
Clearly, this does not tell you much about the alignment of the push button unless you happen to know that Xm.ALIGNMENT_CENTER has a value of 1. Something much more self explanatory would be

>>> print push_button.alignment
Xm.Alignment.Center
It became obvious that a more generic solution to the problem of symbolic constants was in order.

More recently I have been involved in binding a very large C++ class library to Python. Again, the initial results were very promising but there were problems. The requirements for this set of bindings were very similar to those for the Xt bindings. A user who was familiar with the C++ class library should be able to apply their familiarity to the Python implementation. The technique that was chosen to implement the bindings was in many ways similar to the way the Xt bindings were implemented. One built-in object was implemented to represent instances of the class library and a second built-in object was implemented to represent the classes in the class library. The instance object needed to contain a reference to the object that it was to represent and a reference to its class object. Each class object contained a reference to its base class object and the bindings to all of the methods defined on the class but not already defined by any of its base classes. This provided a simple inheritance mechanism which made it unnecessary to re-implement bindings that had already been implemented.

The fundamental problem with this implementation was its inability to allow the derivation, in Python, of new classes from those in the C++ class library. Since, unlike Xt, the C++ library was designed with derivation of new classes as the intended way of building applications, it was essential that a solution be found. The first attempt was straightforward. When binding to a class that has virtual functions, a derived class is created that implements the virtual function. Each virtual function was implemented by adding a Python object attribute to reference the Python function that was to act as the implementation of the virtual function. The binding to this derived class supports methods for setting and getting this attribute. The overall effect is very similar to a widget callback function in Xt. Then, to give the appearance of the ability to derive from the class binding, a Python class is created to act as a wrapper for the built-in class. The __init__ function of the wrapper class is responsible for checking if a virtual function was implemented in a derived class and for setting the callback functions appropriately. There are two obvious problems with this approach:

The first problem shows itself when a wrapped object is either returned from a bound function or passed as a parameter to a virtual "callback" function. Thus it is necessary for the client of the class to know that the object is really just a wrapper around a built-in object. The design philosophy that we are suggesting to our users involves prototyping in Python and then re-implementing the critical components in C or C++. While still theoretically possible, these problems make this approach difficult to implement in practice since much of the code that uses the class would have to be rewritten to compensate for the changes.

The remainder of this paper discusses the approach that was taken to solve the problems that were mentioned above and to support the derivation of built-in classes in Python.


Deriving Built-In Classes in Python - 22 DEC 94
[Next] [Previous] [Top]

Generated with CERN WebMaker